aws-sdk
NPMモジュールを使用してSQSキューにプッシュするアプリのテストカバレッジを記述しようとしていますが、正しくモックする方法がわかりません。
これが今までの私のテストです:
var request = require('superagent'),
expect = require('chai').expect,
assert = require('chai').assert,
sinon = require('sinon'),
AWS = require('aws-sdk'),
app = require("../../../../app");
describe("Activities", function () {
describe("POST /activities", function () {
beforeEach(function(done) {
sinon.stub(AWS.SQS.prototype, 'sendMessage');
done();
});
afterEach(function(done) {
AWS.SQS.prototype.sendMessage.restore();
done();
});
it("should call SQS successfully", function (done) {
var body = {
"custom_activity_node_id" : "1562",
"campaign_id" : "318"
};
reqest
.post('/v1/user/123/custom_activity')
.send(body)
.set('Content-Type', 'application/json')
.end(function(err, res) {
expect(res.status).to.equal(200)
assert(AWS.SQS.sendMessage.calledOnce);
assert(AWS.SQS.sendMessage.calledWith(body));
});
});
});
});
私が見ているエラーは:
1) Activities POST /activities "before each" hook:
TypeError: Attempted to wrap undefined property sendMessage as function
2) Activities POST /activities "after each" hook:
TypeError: Cannot call method 'restore' of undefined
sinon.stub
やJavaScriptでオブジェクトをモックすることに関しては、少しnewbですので、私の無知を許してください
すべてのAWS SDKサービスとメソッドをモックアウトする aws-sdk-mock npmモジュールを作成しました。 https://github.com/dwyl/aws-sdk-mock
それは本当に使いやすいです。サービス、メソッド、およびスタブ関数を指定してAWS.mockを呼び出すだけです。
AWS.mock('SQS', 'sendMessage', function(params, callback) {
callback(null, 'success');
});
次に、以下を呼び出して、テスト後にメソッドを復元します。
AWS.restore('SQS', 'sendMessage');
これは私がsynjsを使ってAWS-SDKをスタブする方法です
import AWS from 'aws-sdk'
import sinon from 'sinon'
let sinonSandbox
const beforeEach = (done) => {
sinonSandbox = sinon.sandbox.create()
done()
}
const afterEach = done => {
sinonSandbox.restore()
done()
}
lab.test('test name', (done) => {
sinonSandbox.stub(AWS, 'SQS')
.returns({
getQueueUrl: () => {
return {
QueueUrl: 'https://www.sample.com'
}
}
})
done()
})
基本的に、メインのSQSからすべてのメソッドを制御します。これが誰かを助けることを願っています
次のようなものを使用して、追加のライブラリを持ち込むことなくそれを行うことができます:
const mocha = require('mocha'),
chai = require('chai'),
expect = chai.expect, // Using Expect style
sinon = require('sinon'),
AWS = require('aws-sdk');
describe('app', function () {
var aws, sqs, app,
sendMessageError = null,
sendMessageData = { MessageId: "1" };
before(() => {
// Create a stub for the SQS lib
sqs = sinon.stub({ sendMessage: Function() });
// Make sure that when someone calls AWS.SQS they get our stub
aws = sinon.stub(AWS, 'SQS');
aws.returns(sqs);
// Now include your app since it will `require` our stubbed version of AWS
app = require('./app');
});
after(() => {
aws.restore(); // Be kind to future tests
});
beforeEach(() => {
// Reset callback behavior after each test
sqs.sendMessage.reset();
// Call the callback supplied to sendMessage in the 1st position with the arguments supplied
sqs.sendMessage.callsArgWith(1, sendMessageError, sendMessageData);
});
it('sends messages', () => {
// Pretend you're using Promises in your app, but callbacks are just as easy
return app.sendMessage().then(() => {
const args = sqs.sendMessage.getCall(0).args[0];
expect(args.QueueUrl).to.be.eq('http://127.0.0.1/your/queue/url');
});
});
});
以下を使用して、SinonでAWS SDKメソッドをスタブできます。
AWS SDKインスタンスをラップし、これを外部から設定できるようにします。
//Within say, SqsService.js
var Aws = require('aws-sdk');
exports.sqsClient = new Aws.SQS({
region: <AWS_REGION>,
apiVersion: <API_VERSION>,
accessKeyId: <AWS_ACCESS_KEY_ID>,
secretAccessKey: <AWS_SECRET_KEY>
});
sqsClient
を使用する場合は、代わりにラップされたインスタンスを使用してください。
var SqsService = require('./SqsService');
function (message, callback) {
//Do stuff..
//Then send stuff..
SqsService.sqsClient.sendMessage(message, callback);
});
したがって、ラップされたAWS SDKを使用して、上からテストケースを変更します。
var request = require('superagent'),
expect = require('chai').expect,
assert = require('chai').assert,
sinon = require('sinon'),
SqsService = require('./SqsService'), //Import wrapper
app = require("../../../../app");
describe("Activities", function () {
describe("POST /activities", function () {
var sendMessageStub;
beforeEach(function(done) {
//Stub like so here
sendMessageStub = sinon.stub(SqsService.sqsClient, 'sendMessage').callsArgWith(1, null, { MessageId: 'Your desired MessageId' });
done();
});
afterEach(function(done) {
sendMessageStub.restore();
done();
});
it("should call SQS successfully", function (done) {
var body = {
"custom_activity_node_id" : "1562",
"campaign_id" : "318"
};
reqest
.post('/v1/user/123/custom_activity')
.send(body)
.set('Content-Type', 'application/json')
.end(function(err, res) {
expect(res.status).to.equal(200)
assert(sendMessageStub.calledOnce);
assert(sendMessageStub.calledWith(body));
});
});
});
});
問題は、AWS SDKクラスがJSON設定から動的に構築されることだと思います。これがSQSの場合です: Github 。
すべてのAPI呼び出しは最終的にmakeRequest
またはmakeUnauthenticatedRequest
on Service になるため、withArgs(...)
を使用してスタブしました。例えば:
var stub = sinon.stub(AWS.Service.prototype, 'makeRequest');
stub.withArgs('assumeRole', sinon.match.any, sinon.match.any)
.yields(null, fakeCredentials);
これは私の簡単な使用例ではうまくいきました。
Sinonでaws sdkをスタブ化できない理由を正確に説明することはできません(たぶん一部のJSエキスパートがそれをよりよく説明できるかもしれません)が proxyquire でうまく機能します。
完全に邪魔にならないまま、テスト中の依存関係のオーバーライドを簡単にするために、nodejsのプロキシが必要です。
上記の@kdlcruzの答えに基づいて、Promiseを使用したいのですが、次のようにします。
import AWS from 'aws-sdk'
import sinon from 'sinon'
let sinonSandbox
const beforeEach = (done) => {
sinonSandbox = sinon.sandbox.create()
done()
}
const afterEach = done => {
sinonSandbox.restore()
done()
}
function mockAWSCall(service, method, expectedArgs, response) {
var stubDef = {};
stubDef[method] = function(args) {
if(expectedArgs) {
expect(args).to.deep.equal(expectedArgs);
}
return {
promise: () => {
return new Promise(function (resolve, reject) {
if(response.startsWith("ERROR:")) {
reject(response);
} else {
resolve(response);
}
});
}
};
};
sinonSandbox.stub(AWS, service).returns(stubDef);
}
lab.test('test name', (done) => {
mockAWSCall('SQS', 'sendMessage', {
MessageBody: 'foo', QueueUrl: 'http://xxx'
}, 'ok');
// Do something that triggers the call...
done()
})