在函数中创建的JavaScript对象的模拟方法



我编写了一个JavaScript函数,它从require((d库创建一个对象,然后使用它。当我试图为它编写测试时,这似乎给我带来了麻烦,因为我似乎没有一个好的方法来控制该对象并创建其方法的mock来测试我的函数的行为。

我遇到这种情况是因为我设计的功能不好吗?我来自Java/Spring背景,所以我脑子里的声音都在尖叫";依赖性注入";。有没有比将函数所需的对象作为参数传递给它更好的方法呢?

示例功能:

// dbService.js
const AWS = require('aws-sdk');
function getItem() {
const dynamo = new AWS.DynamoDB.DocumentClient();
var params = {/* irrelevant */}
try {
return await dynamo.get(getParams).promise();
} catch (err) {
return err;
}
}
exports.getItem = getItem;

dynamo.get()成功返回或抛出错误时,当我试图编写测试来验证函数的行为时,我开始遇到阻塞。

示例测试(我一直用Sinon来嘲讽,用Chai来断言(:

// dbServiceTest.js
const sinon = require('sinon');
const dbService = require('dbService.js');
const expect = require('chai').expect;
describe('dbService: When database returns a record', function() {
let dbMock, dbServiceResp = null;
beforeEach(async function() {
dbMock = sinon.stub(dynamo, "get")
.returns({Item: "an item"});
dbServiceResp = await dbService.getItem("an item");
});
afterEach(function() {
dbMock.restore();
});
it('Should have expected value', function() {
expect(dbServiceResp.Item).to.be.equal("an item");
});
});

很明显,我创建的dynamo.get()的mock没有被dbService.getItem()使用,因为dbService.getItem()完全拥有它自己对DocumentClient对象的依赖关系的实例化。

我应该将DocumentClient传递到getItem()函数中,还是有更好的方法?

DI是最好的方法,它将使代码更易于测试,具有更好的可扩展性,并使模块解耦。但是,如果您想将require模块作为依赖项,您仍然可以存根aws-sdk模块。单元测试解决方案:

dbService.js:

const AWS = require('aws-sdk');
async function getItem() {
const dynamo = new AWS.DynamoDB.DocumentClient();
var params = {
/* irrelevant */
};
try {
return await dynamo.get(params).promise();
} catch (err) {
return err;
}
}
exports.getItem = getItem;

dbService.test.js:

const sinon = require('sinon');
const AWS = require('aws-sdk');
const expect = require('chai').expect;
describe('dbService: When database returns a record', function() {
afterEach(() => {
sinon.restore();
});
it('Should have expected value', async function() {
const mDynamo = { get: sinon.stub().returnsThis(), promise: sinon.stub().resolves({ Item: 'an item' }) };
const mDocumentClient = sinon.stub(AWS.DynamoDB, 'DocumentClient').returns(mDynamo);
const dbService = require('./dbService');
const actual = await dbService.getItem();
expect(actual.Item).to.be.equal('an item');
sinon.assert.calledOnce(mDocumentClient);
sinon.assert.calledWithExactly(mDynamo.get, {});
sinon.assert.calledOnce(mDynamo.promise);
});
it('should return error', async () => {
const mError = new Error('network');
const mDynamo = { get: sinon.stub().returnsThis(), promise: sinon.stub().rejects(mError) };
const mDocumentClient = sinon.stub(AWS.DynamoDB, 'DocumentClient').returns(mDynamo);
const dbService = require('./dbService');
const actual = await dbService.getItem();
expect(actual.message).to.be.eql('network');
sinon.assert.calledOnce(mDocumentClient);
sinon.assert.calledWithExactly(mDynamo.get, {});
sinon.assert.calledOnce(mDynamo.promise);
});
});

单元测试结果:

dbService: When database returns a record
✓ Should have expected value
✓ should return error

2 passing (26ms)
--------------|---------|----------|---------|---------|-------------------
File          | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
--------------|---------|----------|---------|---------|-------------------
All files     |     100 |      100 |     100 |     100 |                   
dbService.js |     100 |      100 |     100 |     100 |                   
--------------|---------|----------|---------|---------|-------------------

相关内容

  • 没有找到相关文章

最新更新