Promiseのテストを補助するライブラリを書いた
promise-test-helper
azu/promise-test-helper という名前そのままですが、
Mocha等でPromiseのテストを書くときに見落としを減らすための補助ライブラリを書きました。
MochaのPromiseテストというのは、下記のようにpromiseオブジェクトを返すとそれをPromiseのテストと認識してやってくれる機構の事を言っています。
it("should support by mocha", function () {
return getSuccessPromise().then(function (value) {
assert(value);
});
});
詳しくは下記を見て下さい。
Promiseのテストの難しさ
Promiseのテストについてはazu/promises-bookのChapter.3 – Promisesのテストで詳しく書いているので、こちらを読んで欲しいのですが簡単にハマりどころを紹介します。
(間違いがあったりもっといい方法があったらIssues · azu/promises-bookにissue下さい)
必ず通ってしまうテスト
例えば、次のようなpromiseオブジェクトがRejectedされた時の結果をテストしたい場合、
必要最小限だと以下のように書いてしまうと思います。
function mayBeRejected(){
return Promise.resolve();
}
it("is bad pattern", function () {
return mayBeRejected().catch(function (error) {
assert(error instanceof Error);
});
});
しかし、このテストはmayBeRejected()
がFulfilledされてしまうと、そもそもcatch
が呼ばれなくなるので必ずテストがパスしてしまいます。
これを防止するためには、promiseオブジェクトがfulfilledされた時は、
テストが失敗するということを明示する必要があります。
function failTest() {
throw new Error("Expected promise to be rejected but it was fulfilled");
}
function mayBeRejected(){
return Promise.resolve();
}
it("thenで落ちる条件を明示する", function () {
return mayBeRejected().then(failTest, function (error) {
assert(error instanceof Error);
});
});
こうすることでfulfilledされた時はfailTest
が呼ばれるのでテストが意図したように落ちます。
(3.2. MochaのPromisesサポートで詳しく解説してます)
promise-test-helperで書く
promise-test-helperではpromiseオブジェクトがどちらの状態になるのかを明示するためのヘルパー関数を提供しています。
var shouldFulfilled = require("promise-test-helper").shouldFulfilled;
var shouldRejected = require("promise-test-helper").shouldRejected;
という2つです。
これを使うと先ほどのテストは以下のように書けます。
var shouldRejected = require("promise-test-helper").shouldRejected;
function mayBeRejected(){
return Promise.resolve();
}
it("should be failed", function () {
return shouldRejected(mayBeRejected()).catch(function (error) {
assert(error instanceof Error);
});
});
この場合は、fulfilledされた時点で自動でテストが落ちるようになるので、自動てテストが通ってしまうミスが減ると思います。
(一番の罠はreturn
忘れですが…)
他のやり方
3.3. 意図したテストを書くにはのまとめでも簡単に触れていますが、
PromiseのテストでもMochaのdone
を使った方法も使えるので、そちらでもPromiseのテストが行えます。
Promise(又は非同期)のテストの難しさには3つのものがあると思います。
- 意図してない状態(onFulfilledかonRejected)になった時にどうするかを明示する必要性
- 意図してない状態になった時にテストが自動で通ってしまう
- MochaのPromisesサポートを利用した際に
return
をし忘れる done
を使ったテストをするときに、仮引数にdone
を書き忘れる
というのが主なものだと思います。
この辺はPromiseの仕様書く人たちでもうっかりすることがあるので、
Promise(又は非同期)のテストでは、”最初に落ちるテストを書く”というのがより重要になるなーと思いました。
他の解決方法として、QUnitのexpect()のようにassertが呼ばれる個数を明示することで、
ミスして自動でパスしてしまうテストを減らす事ができます。
(逆にテストをリファクタリングするときに面倒になる可能性も持っていますが)
今回紹介したpromise-test-helperはazu/promises-bookで使ってるので、サンプル的なものはリポジトリにあるテストを参照するといいかもしれません。
お知らせ欄
JavaScript Primerの書籍版がAmazonで購入できます。
JavaScriptに関する最新情報は週一でJSer.infoを更新しています。
GitHub Sponsorsでの支援を募集しています。