如何测试是否从事件侦听器调用了回调函数?例如,我有以下代码,其中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 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;
无论如何都通过了测试,而不是后者没有通过测试。
有人知道这次测试失败的原因吗?谢谢您的帮助。
答案 0 :(得分:1)
您正在存根之前注册主回调,因此存根函数不是所谓的,而是原始函数。尝试在您的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的main。这样一来,它将永远不会捡到存根。几个解决方案:1)修改main以将UI作为参数(即依赖项注入),在这种情况下,您的测试可以将存根传递给main;或2)require main与proxyquire一起使用,因此您可以强制它要求存根版本。让我知道您是否需要更多详细信息。
答案 1 :(得分:1)
您运行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
方法进行存根,并验证是否已使用适当的值对其进行了调用。然后测试将测试其实际广告。
答案 2 :(得分:0)
好吧,我不关心sinon,但笑话具有称为mock functions的相同功能。
由于出口https://medium.com/@DavideRama/mock-spy-exported-functions-within-a-single-module-in-jest-cdf2b61af642,开玩笑也遇到了同样的问题。由于您在 main.js 中导出了getMain
,init
和start
,并且在{{1}中使用了getMain
和start
}。
相反,请尝试移动init
和getMain
来分离模块并导出并对其进行测试。让我知道问题是否仍然出现
答案 3 :(得分:0)
经过一周的问题和测试,我找到了解决方案。它是DDupont和Louis的解决方案的组合。首先进行以下更改,在 ui.js 文件中,将 if($_POST['submit'] == 'submit'){
$name = $_REQUEST['name'];
echo "Hear".$name;
}
添加到绑定
this.
就像DDupont所说的那样,在单元测试中,将function init(main) {
main.on('http-init', this.onHttpInit.bind(this));
main.on('http-success', this.onHttpSuccess.bind(this));
main.on('http-error', this.onHttpError.bind(this));
main.once('app-ready', this.onAppReady.bind(this));
};
移到存根之后
controller.init(main)
谢谢您的帮助。