Node

Node.jsのuncaughtException, unhandledRejectionのテストを行う

More than 1 year has passed since last update.

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コールバックの例外処理でエラーログを吐く箇所などがあれば、そこで例外を起こすことができる)