我正在尝试为使用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 ,请原谅我的无知
答案 0 :(得分:19)
我们创建了一个aws-sdk-mock npm模块,该模块可以模拟所有AWS SDK服务和方法。 https://github.com/dwyl/aws-sdk-mock
它非常易于使用。只需使用服务,方法和存根函数调用AWS.mock。
AWS.mock('SQS', 'sendMessage', function(params, callback) {
callback(null, 'success');
});
然后通过调用以下方法恢复测试后的方法:
AWS.restore('SQS', 'sendMessage');
答案 1 :(得分:14)
这是我使用sinonjs
存根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中的所有方法。希望这会帮助某人
答案 2 :(得分:2)
您可以使用以下
将带有Sinon的AWS SDK方法存根包装AWS开发工具包实例并允许在外部设置:
//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));
});
});
});
});
答案 3 :(得分:2)
你可以在不使用以下内容引入任何额外库的情况下完成:
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');
});
});
});
答案 4 :(得分:1)
我无法确切地告诉你为什么Sinon不可能存储aws sdk(也许某些JS专家可以更好地解释)但它很适合proxyquire。
代理nodejs的要求是为了在测试过程中轻松实现覆盖依赖,同时保持完全不引人注目。
答案 5 :(得分:1)
我认为问题在于AWS SDK类是从JSON配置动态构建的。这是SQS的一个:Github。
所有API调用最终都会降至Service上的makeRequest
或makeUnauthenticatedRequest
,因此我只使用withArgs(...)
对其进行存根。例如:
var stub = sinon.stub(AWS.Service.prototype, 'makeRequest');
stub.withArgs('assumeRole', sinon.match.any, sinon.match.any)
.yields(null, fakeCredentials);
在我的简单用例中运行良好。
答案 6 :(得分:0)
我喜欢使用承诺,建立在上面@ kdlcruz的答案之后,我做了类似的事情:
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()
})
答案 7 :(得分:0)
使用AWS SDK v3变得更加容易。它甚至可以直接与Promise一起使用,而不必创建嵌入的存根对象。
sinon.stub(SQS.prototype, 'sendMessage').resolves({
SequenceNumber: '0',
});
const sqs = new SQS({});
const result = await sqs.sendMessage({
MessageBody: '',
QueueUrl: '',
});
expect(SQS.prototype.sendMessage).to.be.calledOnce;
expect(result.SequenceNumber).to.be('0');