TL;DR
対象のモジュールをrequireした後にエラーを起こすjsファイルを作成し、 child_process
で呼び出す
詳細
以下のようなモジュール(main.jsとする)のuncaughtExceptionをテストしたいとする
process.on('uncaughtException', err => {
console.log('err!');
process.exit(1);
});
const http = require('http');
const server = http.createServer((req, res) => {
res.end();
});
server.listen(8000);
まず、エラーを発生させるためのjsファイル(error.jsとする)を作る
require('./main');
setTimeout(() => {
throw new Error('test error');
});
次に、テストコードでchild_processを使ってerror.jsを呼び出し、uncaughtExceptionの処理(エラー出力)を確認する。
以下、mochaを想定したテストコード。
const child_process = require('child_process');
const assert = require('assert');
describe('when uncaughtException occurred', function () {
beforeEach(function () {
this.process = child_process.spawn('node', ['error.js']);
});
it('logging message', function (done) {
let msg = '';
// 標準出力を保存しておく
this.process.stdout.on('data', (data) => {
msg += data.toString();
});
// プロセスが終了したとき、標準出力に想定したテキストが出力されているか確認する
this.process.on('close', () => {
assert.equal(msg, 'err!\n');
done();
})
});
});
補足
上の例ではテスト中に一瞬8000番ポートをLISTENしてしまう。これを防ぐには、http.Server.prototype.listen
をmockに置き換えてしまえば良い。
const { Server } = require('http');
Server.prototype.listen = () => {};
require('./main');
setTimeout(() => {
throw new Error('test error');
});
上のファイルは child_process
で呼び出されるため、呼び出し元のプロセスには影響しない。
そのためmock用のライブラリを使わずに直接 http.Server.prototype.listen
を書き換えてしまっている。
また、unhandledRejectionなどのある程度処理が進んで生じるエラーを確認したい場合も同様に、モジュールの挙動を書き換えてunhandledRejectionが起きるようにすれば良い。
(これができるかどうかは元の実装次第になるが、promiseコールバックの例外処理でエラーログを吐く箇所などがあれば、そこで例外を起こすことができる)