我正在尝试为快速节点应用程序创建单元测试。我希望用于测试的配置与用于生产的配置不同,因此我实现了以下内容。
在我的index.js
中,将配置加载到全局变量中,如下所示:
global.env = {};
global.env.config = require('./config/config');
// Create the server ...
server.listen(3000);
module.exports = server;
在其他控制器myController.js
中,我这样访问全局变量
var Config = global.env.config
当我使用node index.js
启动它时,它就可以正常工作。
但是当我将mocha与proxyquire一起使用以覆盖配置时:
describe('myController', function () {
describe("#myMethod", () => {
it("must work", (done) => {
const config = {
INPUT_FILE_DIR: path.resolve('../ressources/input/')
}
const server = proxyquire('../index.js', { './config/config': config })// error in this line
})
})
})
我在告诉myController
无法读取属性配置时出错
Cannot read property 'config' of undefined
感谢您的帮助
为什么不用测试用例中的新配置覆盖它。
例如
index.js
:
const express = require('express');
const server = express();
const userController = require('./userController');
global.env = {};
global.env.config = require('./config');
server.get('/api/user', userController.getUser);
if (require.main === module) {
const port = 3000;
server.listen(port, () => {
console.log(`HTTP server is listening on http://localhost:${port}`);
});
}
module.exports = server;
userController.js
:
const Config = global.env.config;
const userController = {
getUser(req, res) {
res.json(Config.user);
},
};
module.exports = userController;
config.js
:
module.exports = {
user: { name: 'james' },
};
userController.test.js
:
const sinon = require('sinon');
describe('userController', () => {
describe('#getUser', () => {
it('should pass', () => {
global.env = {};
global.env.config = { user: { name: 'jane' } };
const userController = require('./userController');
const mReq = {};
const mRes = { json: sinon.stub() };
userController.getUser(mReq, mRes);
sinon.assert.calledWithExactly(mRes.json, { name: 'jane' });
});
});
});
带有覆盖率报告的单元测试结果:
userController
#getUser
✓ should pass (880ms)
1 passing (893ms)
-------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
userController.js | 100 | 100 | 100 | 100 |
-------------------|---------|----------|---------|---------|-------------------
UPDATE
index.js
:
// Order is matter, assign config to global firstly, then the controllers can access it.
global.env = {};
global.env.config = require('./config');
const express = require('express');
const server = express();
const userController = require('./userController');
server.get('/api/user', userController.getUser);
if (require.main === module) {
const port = 3000;
server.listen(port, () => {
console.log(`HTTP server is listening on http://localhost:${port}`);
});
}
module.exports = server;
[userController.js
和config.js
与上面相同。
index.test.js
:
const request = require('supertest');
const proxyquire = require('proxyquire');
const { expect } = require('chai');
describe('60990025', () => {
it('should get user', (done) => {
const config = { user: { name: 'jane' } };
const server = proxyquire('./', {
'./config': config,
});
request(server)
.get('/api/user')
.expect(200)
.end((err, res) => {
if (err) return done(err);
expect(res.body).to.be.eql({ name: 'jane' });
done();
});
});
});
带有覆盖率报告的API测试结果:
60990025
✓ should get user (2946ms)
1 passing (3s)
-------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|-------------------
All files | 81.25 | 50 | 50 | 81.25 |
config.js | 100 | 100 | 100 | 100 |
index.js | 72.73 | 50 | 0 | 72.73 | 12-14
userController.js | 100 | 100 | 100 | 100 |
-------------------|---------|----------|---------|---------|-------------------
源代码:https://github.com/mrdulin/expressjs-research/tree/master/src/stackoverflow/60990025
这就是我本应采用的方式。首先,我将配置导出为函数而不是对象。
原因是代码将具有更好的结构并且易于维护。另外,也无需全局公开配置,因为这可能会带来一些安全风险。
export const getConfig = () => {
if(process.env.NODE_ENV==="production"){
return require('./production.config');
}
return require('./default.config');
};
在我的测试文件中,我将使用sinonjs
来模拟函数调用,如下所示。
const configModule = require("./config");
sinon.stub(configModule, "getConfig").returns(require('./e2e.config'));
这不是经过测试的代码,但是我有点确定这种思想模式应该可行。