这显然取决于您想要测试代码的范围,但这里有一个样板文件。
一些解释:
- 代码使用
sinon 来存根两个函数:server.close() 和process.exit()。将它们存根意味着不是调用这些函数,而是调用“假函数”(存根),并且您可以断言 if 它被调用;
- 我注释掉了
SIGINT 测试,因为我发现mocha 使用该信号中止测试运行程序。但是,由于SIGTERM 和SIGINT 都使用完全相同的处理程序,那应该没问题;
- 信号传递是异步的,这意味着测试也必须是异步的。我为要测试的信号添加了一个“一次”信号处理程序,当进程收到信号时会调用它;此时,应该调用
stopHandler,做出断言,最后调用done 回调,告诉Mocha 测试已经完成;
代码:
const sinon = require('sinon');
// Load the module to test. This assumes it exports (at least)
// `server` and `settings`, because the test needs them.
let { server, settings } = require('./your-module');
// Don't call the stopHandler when exiting the test.
after(() => {
process.removeAllListeners('SIGTERM');
process.removeAllListeners('SIGINT');
})
describe('Signal handling', () => {
[ 'SIGTERM' /*, 'SIGINT' */ ].forEach(SIGNAL => {
describe(`${ SIGNAL }`, () => {
let sandbox, closeStub, exitStub;
beforeEach(() => {
sandbox = sinon.sandbox.create({ useFakeTimers : true });
closeStub = sandbox.stub(server, 'close');
exitStub = sandbox.stub(process, 'exit');
})
afterEach(() => {
sandbox.restore();
})
it(`should call 'server.close()' when receiving a ${ SIGNAL }`, done => {
process.once(SIGNAL, () => {
sinon.assert.calledOnce(closeStub);
done();
});
process.kill(process.pid, SIGNAL);
})
it(`should call 'process.exit()' after ${ settings.stopTimeout } seconds when receiving a ${ SIGNAL }`, done => {
process.once(SIGNAL, () => {
// It shouldn't have called `process.exit()` right after the signal was sent.
sinon.assert.notCalled(exitStub);
// Advance the clock to a bit after the timeout.
sandbox.clock.tick(settings.stopTimeout + 10);
// At this point, the timeout handler should have triggered, and
// `process.exit()` should have been called.
sinon.assert.calledOnce(exitStub);
// Done.
done();
});
process.kill(process.pid, SIGNAL);
})
})
})
})