Node.js 带有AWS-SDK的Sinon.存根节点
我正在尝试为一个应用程序编写一些测试覆盖范围,该应用程序使用Node.js 带有AWS-SDK的Sinon.存根节点,node.js,sinon,aws-sdk,Node.js,Sinon,Aws Sdk,我正在尝试为一个应用程序编写一些测试覆盖范围,该应用程序使用aws sdkNPM模块将内容推送到SQS队列,但我不确定如何正确模拟内容 以下是我目前的测试: var request = require('superagent'), expect = require('chai').expect, assert = require('chai').assert, sinon = require('sinon'), AWS = require('aws-sdk'),
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中的模拟对象时,我有点新手,所以请原谅我的无知我不能确切地告诉你为什么sinon不可能对aws sdk进行stub(也许一些JS专家可以更好地解释),但它可以很好地使用
为了使测试期间覆盖依赖项变得容易,同时保持完全不受干扰,代理节点需要
您可以使用Sinon将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>
});
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.mock('SQS', 'sendMessage', function(params, callback) {
callback(null, 'success');
});
然后通过调用以下命令在测试后恢复方法:
AWS.restore('SQS', 'sendMessage');
我认为问题在于AWS SDK类是根据JSON配置动态构建的。这是一个用于SQS的示例: 所有的API调用最终都归结为
makeRequest
或makeUnauthenticatedRequest
打开,所以我只是用args(…)删除了那些使用的调用。例如:
var stub = sinon.stub(AWS.Service.prototype, 'makeRequest');
stub.withArgs('assumeRole', sinon.match.any, sinon.match.any)
.yields(null, fakeCredentials);
对于我的简单用例来说,这很好。您可以使用以下内容来完成此操作,而无需引入任何额外的库:
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');
});
});
});
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()
})
这就是我使用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控制所有方法。希望这能帮助某人我喜欢使用承诺,在上面@kdlcruz的答案的基础上,我做了如下事情:
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');
});
});
});
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()
})
有了它,事情变得容易多了。它甚至可以直接使用Promission,而无需创建嵌入的存根对象
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');
您找到解决方案了吗?@hyprstack已经看过/尝试过aws sdk模拟npm模块了吗?(请参阅下面的答案)@nelsonic当时我设法用和sinon将服务存根并使其正常工作。我还没看过aws sdk模拟版。您使用过它吗?@hyprstack是的,我们使用的是aws sdk mock(它简化了Sinon.Stub
):-)谢谢,这很有效!但是我们不能在一个服务中多次模拟一个方法。没错,问题是Sinon在尝试存根/刺探AWS SDK时遇到困难,因此一个解决方案是存根/刺探包装器方法。这对我们来说并不是那么难看,因为我们已经有了一个现有的方法,这样我们就不必一直用键初始化SDK,也不必像Kinesis发布的一个线性示例那样:sinon.stub(AWS,“Kinesis”).returns({putRecord:sinon.stub().callsArgWith(1,null,true)}
不知道为什么这个答案没有得到更多的爱,它很简单,而且可以完成任务。我已经创建了一个处理承诺模式的泛化(我喜欢使用这个模式)——请看下文,在花了几个小时之后,LambdaFinally有什么东西可以做,这对我来说很有用。另外,这是模拟/存根最通用的东西,因为它将覆盖所有aws服务。不知道为什么它没有得到足够的认可。伙计,你应该为此获得一枚奖章。太棒了!JSON定义无疑是个问题。我找不到存根SQS原型的方法,因为函数只绑定到实例。