JavaScriptテストフレームワークのMocha 1.18.0がリリースされ、promiseのテストがサポートされました。

この記事では、次の事について書かれています。

  • Promisesのテストの書き方がどう変わるの?
  • なぜ、フレームワーク側がpromiseのサポートのする必要があるの?
  • 他のPromisesテストのアプローチ

Promisesのテストの書き方がどう変わるの?

例えば、以下のようなpromiseオブジェクトを返す関数をテストしたいと思います。

function getSuccessPromise() {
    return Promise.resolve(true);
}

このpromiseオブジェクトは、resolveするので、.then の第一引数で指定したonFulfilled コールバックに true という値を渡すようになってます。

今までのテストの書き方

このgetSuccessPromiseを 1.18.0より以前は以下のように書くことでテストをしていました。

it("should manually handling test...", function (done) {
    getSuccessPromise().then(function (value) {
        assert(value);
        done();
    }).catch(done); // <= このcatchが今回の変更での焦点
});

getSuccessPromise()の返り値であるpromiseオブジェクトがresolveされると valuetrue が入って assert(true) なのでテストが通るという感じですね。

これの何が問題(面倒)なのかというと、意味がなさそうに見える catch(done) をわざわざ書いてるのから分かるかもしれませんが、テストが失敗した時に結果をdoneに渡す必要があります。

何故かというと、assertの結果がfailの場合は例外が投げられますが、promisesの.thenの中で例外が投げられても自動的に例外がキャッチされてしまいます。

もし、.catch(done);を書かなかった場合、テストが失敗することもないですし、doneも呼ばれないため終わらないテストが出来上がってしまいます。(タイムアウトになるテスト)

このことを忘れると簡単に無意味なテストが出来上がったりしてしまうため結構はまりやすいです。

例えば、次のようなテストはタイムアウトが来るまで終わらなくなります。

it("is 終わらないテスト", function (done) {
    Promise.resolve(false).then(function (value) {
        assert(value);
        done();
    });
});

promisesのテストサポート

これがどのように書けるようになったかというと、公式のドキュメントにも載ってるのでそちらも参考にして欲しいのですが、以下のように書けるようになりました。

it("should support by mocha", function () {
    return getSuccessPromise().then(function (value) {
        assert(value);
    });
});

上記のようにdoneがテストから消えてることがわかります。
代わりに、promiseオブジェクトをテスト内でreturnして渡しています。

ちゃんとassertがfailの場合はテストもfailとなり、テストコードに無駄がなくなっています。

いわゆるthenableオブジェクト、thenメソッドを持ったオブジェクトをreturnした場合、それはpromiseのテストだと判断してくれます。

以下にサンプルコードを置いてあります。(主に失敗した時の問題がわかるように失敗するテストが混じってます)

azu/mocha-support-promise

その他

このテスト内で promiseオブジェクトをreturn してテストできるものとしてはBuster.JS等があります。
(他にpromisesをサポートしてるフレームワークとかあるかな?)

mochaの該当pull request見ても分かるように結構歴史ある話ですが、最近になってECMAScriptとしてPromisesが仕様に入ってきたのもあって需要が出てきてサポートされた感じに見えます。

title="Adding promise support to runnables (and thus tests). by domenic · Pull Request #329 · visionmedia/mocha"> it is a community project so it's kinda up to you guys

Adding promise support to runnables (and thus tests). by domenic · Pull Request #329 · visionmedia/mocha

その他のpromisesサポートの方法としては、domenic/mocha-as-promised等のassertを拡張したライブラリ使うことなどでも、シンプルに書くことができるかもしれません。

as-promisedで検索すると色々見つかると思います。

Promises Bookのテストを書いてて、同じ事を思ってたのでmocha自体がサポートしてくれてちょうど良かったです。

Promisesの強力なエラーハンドリングが、反ってテスト時には邪魔になってしまう事があるという面白い感じの話だと思います。