jestにおけるアサーション数の制限について
jest/max-expects
eslint-plugin-jestに、テスト毎にexpectの回数を制限するmax-expectsのルールを提案して、Pull Requestを出して、v26.6.0でリリースしてもらった。
なのでeslint-plugin-jestのv26.6.0からjest/max-expectsをrulesに追加すると
{
...,
"rules": {
...,
+ "jest/max-expects": ["error", { "max": 2 }]
}
}
testの中でmaxオプションの上限値を超えてexpectを使ってアサーションしている箇所では、以下のようにESLintがエラーを報告するようになった。
Too many assertion calls (3). Maximum allowed is 2.
maxオプションのデフォルトは5で、これはava/max-assertsのオプションのデフォルト値を参考にしている。
検知できる範囲としてあくまでAST解析して見分けられるものが対象だと思うので、for文のような形で繰り返しexpectが実行されるようなケースまではカバーできない認識(prefer-eachのルールが追加されると、それらのルールの組み合わせで結果的に防げるようになるかも?)。
テストにおけるアサーションの数について
上述のESLintのルールは、jestのテストにおけるアサーション数を制限するものだけど、そもそもテストのアサーション数が少ないことを望ましいとする背景は何か。
Assertion Roulette
XUnit Test Patternsでは、Behavior Smells(テスト実行中の臭い)のカテゴリで紹介されているAssertion Rouletteがある。
これはテストランナーが失敗を出力しても、どのアサーションで失敗したかを特定できないような状況をさしている。
A test fails. Upon examining the output of the Test Runner (page X), we cannot determine exactly which assertion had failed.
明確にそれぞれアサーション毎にテストを分けた方が失敗した時に原因の切り分けがしやすいということがある。
また、失敗したあとのアサーション結果を隠してしまう問題がある。
例えば、1つのテストで3回expectを実行している場合に、2つ目のexpectで失敗すると3つ目のexpectはテスト結果が分からない(そうじゃないテストランナーもあるのかもしれない?)ので、失敗かもしれないしそうじゃないかかもしれない。
一般的なテスティングフレームワークでは、アサーション失敗時に例外が発生し、そのテストメソッドの実行は打ち切られます。
1つのテストに1つのアサーションが理想的という考え方がある。ただ、あくまで理想的にはということであって、1つのテストに必ずアサーションを1つとするのではなく論理的なコンセプトを1つとするくらいで考えるべきかもしれない。
My guideline is usually that you test one logical CONCEPT per test. you can have multiple asserts on the same object. they will usually be the same concept being tested.
Discussion