如何测试是否调用了事件侦听器的回调函数?例如,我有以下代码,其中app.js通过init.js控制器初始化应用程序。
main.js文件有一个类,它扩展和事件发射器,使对象成为事件发射器。
app.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,
};init.js
exports.startMain = () => {
console.log('Start application');
// Load application modules
const main = require('./main');
// Start the application
main.init();
};因此,当我运行命令node app.js时,我看到以下输出
启动应用程序HTTP INIT序列HTTP成功序列HTTP错误序列
这意味着侦听器处于活动状态,函数被调用。
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断言始终失败,说明如下:
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;都会通过测试,而不是后者失败。
有人知道这次考试不及格的原因吗?谢谢你的帮助。
发布于 2019-09-10 18:47:48
运行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));
}从这段代码的角度来看,调用( onHttpInit**,)是由调用( sinon.stub(controller, value) )产生的,但并不改变上面代码中的符号(、ui **、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方法进行存根,并验证是否已经用适当的值调用了它。然后,测试将测试它实际上所宣传的内容。
发布于 2019-08-27 16:11:10
在挂起之前要注册主回调,所以存根函数不是所谓的函数,而是原始函数。尝试在您的it函数中颠倒顺序:
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);
})
})它还显示,您需要主,而不使用proxyquire。这样它就永远不会捡起存根了。有几种解决方案: 1)将UI作为参数(即依赖项注入)重新工作main,在这种情况下,您的测试可以将存根传递给main;或者2)使用proxyquire要求main,这样就可以强制它要求存根版本。如果你需要更多细节,请告诉我。
发布于 2019-09-10 11:36:09
好吧,我不是关于sinon的,但是这个玩笑有相同的功能,叫做模拟函数。
由于出口https://medium.com/@DavideRama/mock-spy-exported-functions-within-a-single-module-in-jest-cdf2b61af642,jest也面临着同样的问题。因为您导出了getMain、init和start在main.js中,并且在init中使用了getMain和start。
相反,尝试将getMain和start移动到单独的模块,并导出和测试它。如果问题还在出现,请告诉我
https://stackoverflow.com/questions/57657332
复制相似问题