訳注
これはwhen-to-use-plan.mdの日本語訳です。こちらがAVAのmasterブランチとの差分のリンクになります(このリンクをクリックして、when-to-use-plan.md
に変更点が見当たらなければ、この翻訳が最新であることを意味します)。
AVAとtap
/tape
の主な違いはt.plan()
の振る舞いです。AVAでt.plan()
は予定されたアサーションが実行される回数と一致するかどうかを確認するときのみ利用されます。これによるテストの自動終了はしていません。
tap
/tape
になれた多くのユーザはt.plan()
をすべてのテストごとに使う傾向があります。しかし、AVAではこれを「ベストプラクティス」だと考えていません。その代わりに、t.plan()
が何かしらの値を意味するときだけに使われるべきと信じています。
t.plan()
はほとんどの同期テストに必要ありません。
test(t => {
// 望ましくない: ここには分岐がない - t.plan()が無意味である
t.plan(2);
t.is(1 + 1, 2);
t.is(2 + 2, 4);
});
この場合t.plan()
は何の意味もなくて、無駄な作業を増やしています。
test(t => {
t.plan(1);
return somePromise().then(result => {
t.is(result, 'foo');
});
});
ちらっと見ると、このテストには非同期のPromiseが使われているのでt.plan()
を使って良いように見えます。しかし、ここにはいくつかの問題があります:
-
t.plan()
はたぶんsomePromise()
がrejectされる可能性から守るために使われていると思います。しかし、rejectされたpromiseが返されるとどのみちテストは失敗するようになります。 -
async
/await
の長所を活かしたほうがいいはずです:
test(async t => {
t.is(await somePromise(), 'foo');
});
t.plan()
が好ましい場合も多いです。
test(t => {
t.plan(2);
return shouldRejectWithFoo().catch(reason => {
t.is(reason.message, 'Hello') // メッセージだけが気になるならt.throws()を使ってください
t.is(reason.foo, 'bar');
});
});
ここでのt.plan()
はcatch
の中のコードが実行されるのかを保証してくれます。殆どの場合、t.throws()
を使ったほうがいいが、t.throws()
はエラーのmessage
プロパティしか確認できないので有効な使い方です。
test(t => {
t.plan(2);
try {
shouldThrow();
} catch (err) {
t.is(err.message, 'Hello') // メッセージだけが気になるならt.throws()を使ってください
t.is(err.foo, 'bar');
}
});
上のtry
/catch
の例のように、だいたいt.throws()
がいい選択です。しかし、これはmessage
プロパティしか確認できません。
test.cb(t => {
t.plan(2);
const callbackA = () => {
t.pass();
t.end();
};
const callbackB = () => t.pass();
bThenA(callbackA, callbackB);
});
上のコードはcallbackA
の次に来るcallbackB
が一番最初に(そして、一回だけ)呼ばれたのかを確認します。それ以外の組み合わせは失敗します。
殆どの場合、複雑な分岐をテストに入れるのは良くないアイディアです。明らかな例外は(JSONなどから)自動生成されたテストぐらいです。下記のt.plan()
はJSONが正しいのかを確認するために使われています。
const testData = require('./fixtures/test-definitions.json');
testData.forEach(testDefinition => {
test(t => {
const result = functionUnderTest(testDefinition.input);
// testDefinitionは`foo`と`bar`の両方ではなく、いずれかが期待値であるべきです。
if (testDefinition.foo) {
t.is(result.foo, testDefinition.foo);
}
if (testDefinition.bar) {
t.is(result.bar, testDefinition.foo);
}
});
});
t.plan()
は多くの有効な使い方がありますが、分別なく使うのはいけません。おおよそ、テストが直線的ではなくて、簡単に推論できず、コードフローもない場合に使った方がいいです。コールバックの中のアサーション、if
/then
文、for
/while
文、(場合によっては)try
/catch
がt.plan()
を使ったほうがいい候補となります。