javascript - NodeJS:如何断言是否使用 sinon 调用了事件回调函数
问题描述
如何测试是否调用了来自事件侦听器的回调函数?例如,我有以下代码,其中 app.js 通过 init.js 控制器初始化应用程序。
main.js 文件有一个扩展类和事件发射器,使对象成为事件发射器。
应用程序.js
const initController = require('./init');
async function init() {
initController.startMain();
}
init();
main.js
const events = require('events'),
ui = require('./ui');
module.exports.getMain = function () {
class Main extends events.EventEmitter {
constructor() {
super();
this.status = null;
}
}
return new Main();
};
module.exports.init = () => {
const main = this.getMain();
ui.init(main);
this.start(main);
}
module.exports.start = (main) => {
ui.start(main);
main.emit('http-init');
main.emit('http-success');
main.emit('http-error');
};
ui.js
function init(main) {
main.on('http-init', onHttpInit.bind(this));
main.on('http-success', onHttpSuccess.bind(this));
main.on('http-error', onHttpError.bind(this));
main.once('app-ready', onAppReady.bind(this));
};
function start (main) {};
function onAppReady() {
console.log('APP READY');
};
function onHttpInit() {
console.log('HTTP INIT SEQUENCE');
};
function onHttpError(error) {
console.log('HTTP ERROR SEQUENCE');
};
function onHttpSuccess() {
console.log('HTTP SUCCESS SEQUENCE');
};
module.exports = exports = {
init,
start,
onHttpInit,
onHttpError,
onHttpSuccess,
};
初始化.js
exports.startMain = () => {
console.log('Start application');
// Load application modules
const main = require('./main');
// Start the application
main.init();
};
所以,当我运行命令时node app.js
,我看到以下输出
启动应用程序 HTTP INIT SEQUENCE HTTP SUCCESS SEQUENCE HTTP ERROR SEQUENCE
这意味着侦听器处于活动状态并且函数被调用。
ui.tests.js
const sinon = require('sinon'),
main = require('../main').getMain(),
proxyquire = require('proxyquire').noPreserveCache().noCallThru();
describe('UI Tests', () => {
const sandbox = sinon.createSandbox();
let controller = null;
before(() => {
controller = proxyquire('../ui', {});
})
describe('Testing Eventlisteners', ()=> {
afterEach(() => {
main.removeAllListeners();
});
const eventMap = new Map([
[ 'http-init', 'onHttpInit' ],
[ 'http-success', 'onHttpSuccess' ],
[ 'http-error', 'onHttpError']
]);
eventMap.forEach((value, key) => {
it(`should register an eventlistener on '${key}' to ${value}`, () => {
const stub = sinon.stub(controller, value);
controller.init(main);
main.emit(key);
sinon.assert.called(stub);
})
})
})
})
但是,当我运行上述测试时,即使我得到了输出,即调用了函数,但是 sinon assert 总是失败,如下所示:
UI Tests
Testing Eventlisteners
HTTP INIT SEQUENCE
1) should register an eventlistener on 'http-init' to onHttpInit
HTTP SUCCESS SEQUENCE
2) should register an eventlistener on 'http-success' to onHttpSuccess
HTTP ERROR SEQUENCE
3) should register an eventlistener on 'http-error' to onHttpError
0 passing (16ms)
3 failing
1) UI Tests
Testing Eventlisteners
should register an eventlistener on 'http-init' to onHttpInit:
AssertError: expected onHttpInit to have been called at least once but was never called
at Object.fail (node_modules/sinon/lib/sinon/assert.js:106:21)
at failAssertion (node_modules/sinon/lib/sinon/assert.js:65:16)
at Object.assert.(anonymous function) [as called] (node_modules/sinon/lib/sinon/assert.js:91:13)
at Context.it (test/ui.tests.js:25:30)
2) UI Tests
Testing Eventlisteners
should register an eventlistener on 'http-success' to onHttpSuccess:
AssertError: expected onHttpSuccess to have been called at least once but was never called
at Object.fail (node_modules/sinon/lib/sinon/assert.js:106:21)
at failAssertion (node_modules/sinon/lib/sinon/assert.js:65:16)
at Object.assert.(anonymous function) [as called] (node_modules/sinon/lib/sinon/assert.js:91:13)
at Context.it (test/ui.tests.js:25:30)
3) UI Tests
Testing Eventlisteners
should register an eventlistener on 'http-error' to onHttpError:
AssertError: expected onHttpError to have been called at least once but was never called
at Object.fail (node_modules/sinon/lib/sinon/assert.js:106:21)
at failAssertion (node_modules/sinon/lib/sinon/assert.js:65:16)
at Object.assert.(anonymous function) [as called] (node_modules/sinon/lib/sinon/assert.js:91:13)
at Context.it (test/ui.tests.js:25:30)
我不知道为什么测试会失败,即使该函数至少被调用了一次,这是从输出中看到的HTTP INIT SEQUENCE
,HTTP SUCCESS SEQUENCE
并且HTTP ERROR SEQUENCE
当我运行测试时。
我试着做stub.should.have.been.called;
。然而,有了这个测试通过,它并没有真正通过测试,stub.should.have.been.called;
或者无论如何都stub.should.not.have.been.called;
通过了测试,而不是后者没有通过测试。
有人知道这次测试失败的原因吗?感谢您的任何帮助。
解决方案
您运行const stub = sinon.stub(controller, value);
以存根ui
模块导出的值。这确实会更改模块导出的值,但问题在于模块中的这段代码ui
:
function init(main) {
main.on('http-init', onHttpInit.bind(this));
main.on('http-success', onHttpSuccess.bind(this));
main.on('http-error', onHttpError.bind(this));
main.once('app-ready', onAppReady.bind(this));
}
从这个代码的角度来看,module.exports
你的调用改变了这段代码,sinon.stub(controller, value)
但这不会改变上面代码中的符号、等符号的值,onHttpInit
因为onHttpSuccess
这些符号是ui
模块范围内的局部符号。你可以随心所欲地变异module.exports
:它仍然对上面的代码没有影响。
您可以将代码更改为:
function init(main) {
main.on('http-init', exports.onHttpInit.bind(this));
main.on('http-success', exports.onHttpSuccess.bind(this));
main.on('http-error', exports.onHttpError.bind(this));
main.once('app-ready', exports.onAppReady.bind(this));
}
您可以exports
直接使用,因为您为两者分配了相同的module.exports
值exports
module.exports = exports = ...
此更改应该可以解决您遇到的直接问题。但是,我会在这里修改测试方法。您的测试有标题should register an eventlistener on '${key}' to ${value}
,但实际上您正在测试的不仅仅是事件侦听器已注册,而且事件传播有效。实际上,您正在测试EventEmitter
负责提供的功能。我将更改测试以存根对象的on
方法main
并验证它是否已使用适当的值调用。然后测试将测试它实际宣传的内容。
推荐阅读
- python - AWS Glue 完成后,如何执行 SQL 脚本或存储过程?
- gps - 为什么当串行数据流经 socat 时,chrony 不会同步到 GPS?
- spring - 了解 Spring Security @EnableOAuth2Client 注解
- c# - 如何在 .Net Core 中的 CSV 导出期间处理大型列表
- asp.net - 使用 Razor 语法在 ASP.Net Core 中添加多组键值对数据
- python - pip 安装到 python 3.6 文件夹而不是 python 2.7 文件夹
- spring-data-jpa - JPA本机查询以查找当前日期的记录
- ios - 快速制作重试屏幕
- c# - C# - DateTime.Now.TimeOfDay 在我的其他计算机上相差 2 秒,我如何获得准确的时间?
- google-cloud-platform - 使用服务账号认证谷歌云平台时如何打印错误码?