There is an example of my graceful shutdown:
function stopHandler() {
logger.info(`Stopping server on port ${settings.port} with pid ${process.pid} started`);
server.close(() => {
logger.info(`Server on port ${settings.port} with pid ${process.pid} stopped`);
process.exit();
});
setTimeout(() => {
logger.info(`Server on port ${settings.port} with pid ${process.pid} stop forcefully`);
process.exit();
}, settings.stopTimeout);
};
process.on("SIGTERM", stopHandler);
process.on("SIGINT", stopHandler);
How I can test this code with mocha?
It obviously depends how extensively you want to test your code, but here's a boilerplate to start with.
Some explanation:
sinon
to stub two functions: server.close()
and process.exit()
. Stubbing them means that instead of calling those functions, a "fake function" (the stub) gets called, and you can assert if it got called;SIGINT
test because I found out that mocha
uses that signal to abort the test runner. However, since both SIGTERM
and SIGINT
use the exact same handler, that should be okay;stopHandler
should have been called, make your assertions, and at the end call the done
callback to tell Mocha that the test has completed;The code:
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);
})
})
})
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With