当函数内部需要模块时使用 Sinon 的函数存根
Function stub with Sinon when modules are required inside functions
我正在尝试将函数存根以进行单元测试,但我不确定这是否可行,或者我是否应该在能够执行此操作之前进行更改。我会尽力解释情况
文件aController.js
...
module.exports = (sqlConnection) => {
...
return {
...
aControllerFunction,
...
}
function aControllerFunction(req, res, next) {
const aService = require('../services/aService')(sqlConnection, req.models)
...
aService.aServiceFunction(req.a, req.b)
}
...
}
...
文件aService.js
...
module.exports = (sqlConnection, models) => {
return {
...
aServiceFunction,
...
}
...
function aServiceFunction(a, b) {
...
models.aModel.update(a)
sqlConnection.queryAsync(`UPDATE... ${a}`)
...
}
...
}
...
我想对函数 aControllerFunction
和 aServiceFunction
进行单元测试。
对于 aControllerFunction
我应该存根 aService.aServiceFunction
,对于 aServiceFunction.aServiceFunction
我应该存根 sqlConnection.queryAsync
和 models.aModel.update
.
这种结构是否可行,还是我应该先更改它?我尝试这样做,但我发现很难存根,因为要求在函数内部。
为了测试 aControllerFunction
,我们需要额外的包 proxyquire. We can use this package to mock function(serviceFactory
) exported from a module. This called link seams。
例如
controllers/aController.js
:
module.exports = (sqlConnection) => {
return {
aControllerFunction,
};
function aControllerFunction(req, res, next) {
const aService = require('../services/aService')(sqlConnection, req.models);
aService.aServiceFunction(req.a, req.b);
}
};
controllers/aController.test.js
:
const proxyquire = require('proxyquire');
const sinon = require('sinon');
describe('aController', () => {
it('should pass', () => {
const sqlConnection = {};
const req = { models: {}, a: 'a', b: 'b' };
const aService = { aServiceFunction: sinon.stub() };
const serviceFactory = sinon.stub().returns(aService);
const ControllerFactory = proxyquire('./aController', {
'../services/aService': serviceFactory,
});
const { aControllerFunction } = ControllerFactory(sqlConnection);
aControllerFunction(req);
sinon.assert.calledWithExactly(serviceFactory, {}, {});
sinon.assert.calledWithExactly(aService.aServiceFunction, 'a', 'b');
});
});
services/aService.js
:
module.exports = (sqlConnection, models) => {
return {
aServiceFunction,
};
function aServiceFunction(a, b) {
models.aModel.update(a);
sqlConnection.queryAsync(`UPDATE... ${a}`);
}
};
services/aService.test.js
:
const serviceFactory = require('./aService');
const sinon = require('sinon');
describe('aService', () => {
it('should pass', () => {
const sqlConnection = {
queryAsync: sinon.stub(),
};
const models = {
aModel: {
update: sinon.stub(),
},
};
const { aServiceFunction } = serviceFactory(sqlConnection, models);
aServiceFunction('a', 'b');
sinon.assert.calledWithExactly(models.aModel.update, 'a');
sinon.assert.calledWithExactly(sqlConnection.queryAsync, 'UPDATE... a');
});
});
带有覆盖率报告的单元测试结果:
aController
✓ should pass
aService
✓ should pass
2 passing (35ms)
-----------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
controllers | 100 | 100 | 100 | 100 |
aController.js | 100 | 100 | 100 | 100 |
services | 100 | 100 | 100 | 100 |
aService.js | 100 | 100 | 100 | 100 |
-----------------|---------|----------|---------|---------|-------------------
我正在尝试将函数存根以进行单元测试,但我不确定这是否可行,或者我是否应该在能够执行此操作之前进行更改。我会尽力解释情况
文件aController.js
...
module.exports = (sqlConnection) => {
...
return {
...
aControllerFunction,
...
}
function aControllerFunction(req, res, next) {
const aService = require('../services/aService')(sqlConnection, req.models)
...
aService.aServiceFunction(req.a, req.b)
}
...
}
...
文件aService.js
...
module.exports = (sqlConnection, models) => {
return {
...
aServiceFunction,
...
}
...
function aServiceFunction(a, b) {
...
models.aModel.update(a)
sqlConnection.queryAsync(`UPDATE... ${a}`)
...
}
...
}
...
我想对函数 aControllerFunction
和 aServiceFunction
进行单元测试。
对于 aControllerFunction
我应该存根 aService.aServiceFunction
,对于 aServiceFunction.aServiceFunction
我应该存根 sqlConnection.queryAsync
和 models.aModel.update
.
这种结构是否可行,还是我应该先更改它?我尝试这样做,但我发现很难存根,因为要求在函数内部。
为了测试 aControllerFunction
,我们需要额外的包 proxyquire. We can use this package to mock function(serviceFactory
) exported from a module. This called link seams。
例如
controllers/aController.js
:
module.exports = (sqlConnection) => {
return {
aControllerFunction,
};
function aControllerFunction(req, res, next) {
const aService = require('../services/aService')(sqlConnection, req.models);
aService.aServiceFunction(req.a, req.b);
}
};
controllers/aController.test.js
:
const proxyquire = require('proxyquire');
const sinon = require('sinon');
describe('aController', () => {
it('should pass', () => {
const sqlConnection = {};
const req = { models: {}, a: 'a', b: 'b' };
const aService = { aServiceFunction: sinon.stub() };
const serviceFactory = sinon.stub().returns(aService);
const ControllerFactory = proxyquire('./aController', {
'../services/aService': serviceFactory,
});
const { aControllerFunction } = ControllerFactory(sqlConnection);
aControllerFunction(req);
sinon.assert.calledWithExactly(serviceFactory, {}, {});
sinon.assert.calledWithExactly(aService.aServiceFunction, 'a', 'b');
});
});
services/aService.js
:
module.exports = (sqlConnection, models) => {
return {
aServiceFunction,
};
function aServiceFunction(a, b) {
models.aModel.update(a);
sqlConnection.queryAsync(`UPDATE... ${a}`);
}
};
services/aService.test.js
:
const serviceFactory = require('./aService');
const sinon = require('sinon');
describe('aService', () => {
it('should pass', () => {
const sqlConnection = {
queryAsync: sinon.stub(),
};
const models = {
aModel: {
update: sinon.stub(),
},
};
const { aServiceFunction } = serviceFactory(sqlConnection, models);
aServiceFunction('a', 'b');
sinon.assert.calledWithExactly(models.aModel.update, 'a');
sinon.assert.calledWithExactly(sqlConnection.queryAsync, 'UPDATE... a');
});
});
带有覆盖率报告的单元测试结果:
aController
✓ should pass
aService
✓ should pass
2 passing (35ms)
-----------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
controllers | 100 | 100 | 100 | 100 |
aController.js | 100 | 100 | 100 | 100 |
services | 100 | 100 | 100 | 100 |
aService.js | 100 | 100 | 100 | 100 |
-----------------|---------|----------|---------|---------|-------------------