LoginSignup
11
10

More than 5 years have passed since last update.

Bluebirdを利用したコードをMochaでテストする

Posted at

Mochaを使ってBluebirdを利用したコードのテストを行おうとした際、正しくテスト結果が得られない場合があったので原因と対策をメモ。

まず、バグを含んだ関数を用意する。

promise-add.js
var Promise = require('bluebird')

module.exports = function promiseAdd(a, b) { // 2つの値を足し算する関数
  return new Promise(function(resolve, reject) {
    resolve(a + b + 1) // 正しい結果に+1されてしまうバグ!
  })
}

次に、上記の関数のテストを用意する。

promise-add-test.js
var assert = require('assert')
var promiseAdd = require('./promise-add.js')

describe('promise-add', function() {
  it('promiseAdd(1, 1) == 2', function(done) { // promiseAdd(1, 1)の結果が2であることをテストする
    promiseAdd(1, 1).then(function(result) {
      assert.equal(result, 2) // result == 3なので、テスト失敗!
      done()
    })
  })
})

結果は、timeoutとなり期待していた結果(AssertionError)が得られない。

Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.

さらに、例外として「Umhandled rejection」が投げられてしまう。

Unhandled rejection AssertionError: 3 == 2
    at promise-add-test.js:7:14
    at tryCatcher (node_modules/bluebird/js/main/util.js:26:23)
    at Promise._settlePromiseFromHandler (node_modules/bluebird/js/main/promise.js:503:31)
    at Promise._settlePromiseAt (node_modules/bluebird/js/main/promise.js:577:18)
    at Promise._settlePromiseAtPostResolution (node_modules/bluebird/js/main/promise.js:244:10)
    at Async._drainQueue (node_modules/bluebird/js/main/async.js:128:12)
    at Async._drainQueues (node_modules/bluebird/js/main/async.js:133:10)
    at Immediate.Async.drainQueues [as _onImmediate] (node_modules/bluebird/js/main/async.js:15:14)
    at processImmediate [as _immediateCallback] (timers.js:358:17)
    1) 1 + 1 = 2

原因

bluebirdのPromiseは、例外が投げられたりreject関数が呼ばれたりするとcatch handlerに飛ぶ仕組みになっている。そのため、assertを呼び出しエラーが起きた時点でcatch handlerに飛ぼうとするため、後続のdoneが実行されずにtimeoutとなる。
さらに、catch handlerを記述していないため「Unhandled rejection」が表示されてしまう。

promise-add-test.js
var assert = require('assert')
var promiseAdd = require('./promise-add.js')

describe('promise-add', function() {
  it('promiseAdd(1, 1) == 2', function(done) { // promiseAdd(1, 1)の結果が2であることをテストする
    promiseAdd(1, 1).then(function(result) {
      assert.equal(result, 2) // result == 3なので、テスト失敗! -> catch handlerへ飛ぶ
      done() // !!!呼ばれない!!!
    })
  })
})

対策

assertによって投げられたエラーは後続のcatch handlerで受け取れるので、catch handlerの中でdoneを呼び出すように変更する。

promise-add-test.js
var assert = require('assert')
var promiseAdd = require('./promise-add.js')

describe('promise-add', function() {
  it('promiseAdd(1, 1) == 2', function(done) { // promiseAdd(1, 1)の結果が2であることをテストする
    promiseAdd(1, 1).then(function(result) {
      assert.equal(result, 2) // result == 3なので、テスト失敗! -> catch handlerへ飛ぶ
      done() // !!!呼ばれない!!!
    }).catch(function (e) { // catch handlerを追加
      done(e) // 呼ばれる
    })
  })
})

これを実行すると、timeoutにならずきちんとAssertionErrorが表示される。

AssertionError: 3 == 2
+ expected - actual

-3
+2
11
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10