I am trying to stub functions to unit test, but I am not sure if this is possible or I should do changes before I am able to do it. I will try to explain the situation
file 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)
}
...
}
...
file aService.js
...
module.exports = (sqlConnection, models) => {
return {
...
aServiceFunction,
...
}
...
function aServiceFunction(a, b) {
...
models.aModel.update(a)
sqlConnection.queryAsync(`UPDATE... ${a}`)
...
}
...
}
...
I want to unit test the functions aControllerFunction
and aServiceFunction
.
For aControllerFunction
I should stub aService.aServiceFunction
, and for aServiceFunction.aServiceFunction
I should stub sqlConnection.queryAsync
and models.aModel.update
.
Is this possible with this structure, or should I change it before? I tried to do it, but I find hard to stub because the requires are inside the functions.
In order to testing aControllerFunction
, we need additional package proxyquire. We can use this package to mock function(serviceFactory
) exported from a module. This called link seams.
E.g.
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');
});
});
unit test result with coverage report:
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 |
-----------------|---------|----------|---------|---------|-------------------