単体テストで複数のアサーションを使用するのは悪い習慣ですか?それは重要ですか?
8 に答える
テストケースごとに1つだけあることもありますが、多くの場合、複数のステートメントassert
があると思います。assert
@Arkainがとらえどころのないケースを見てきました。この場合、非常に大きなコードには、いくつかのテストケースを含む単一の単体テストスイートがあり、それらはすべて、などのラベルが付けられtestCase1
、testCase2
各テストケースには数百のアサートがあります。さらに良いことに、各条件は通常、以前の実行の副作用に依存します。ビルドが失敗するたびに、常にそのような単体テストでは、問題がどこにあるかを判断するのにかなりの時間がかかります。
しかし、もう1つの極端な例は、あなたの質問が示唆していることです。考えられる条件ごとに個別のテストケースです。テストする内容によっては、これは理にかなっているかもしれませんが、多くassert
の場合、テストケースごとに数個あります。
たとえば、を書いた場合java.lang.Integer
、次のような場合があります。
public void testValueOf() {
assertEquals(1, Integer.valueOf("1").intValue());
assertEquals(0, Integer.valueOf("0").intValue());
assertEquals(-1, Integer.valueOf("-1").intValue());
assertEquals(Integer.MAX_VALUE, Integer.valueOf("2147483647").intValue());
assertEquals(Integer.MIN_VALUE, Integer.valueOf("-2147483648").intValue());
....
}
public void testValueOfRange() {
assertNumberFormatException("2147483648");
assertNumberFormatException("-2147483649");
...
}
public void testValueOfNotNumbers() {
assertNumberFormatException("");
assertNumberFormatException("notanumber");
...
}
private void assertNumberFormatException(String numstr) {
try {
int number = Integer.valueOf(numstr).intValue();
fail("Expected NumberFormatException for string \"" + numstr +
"\" but instead got the number " + number);
} catch(NumberFormatException e) {
// expected exception
}
}
テストケースに入れるアサートの数について、私がすぐに考えることができるいくつかの簡単なルール:
assert
以前の実行の副作用に依存するものを複数持つことはできません。assert
同じ機能/機能またはそのファセットをテストするグループをまとめます。必要がない場合は、複数の単体テストケースのオーバーヘッドは必要ありません。- 上記のルールはいずれも、実用性と常識によって上書きする必要があります。おそらく、それぞれに1つのアサーション(または複数のアサーション)を持つ1000の単体テストケースは必要ありません。また、数百の
assert
ステートメントを持つ単一のテストケースは必要ありません。
いいえ、それは悪い習慣ではありません。テストしているメソッドがクラスを返す場合は、設定されているはずのさまざまな変数をテストする必要があります。この目的のために、1 つの単体テストを使用することもできます。
ただし、1 つの単体テストで複数の機能をテストしている場合、失敗したときにどの機能が問題を引き起こしたのかが明確になりません。単体テストはあなたの友達であることを忘れないでください。何がうまくいかなかったのかを簡単に確認できるようにして、修正できるようにします。
単体テストは適度にきめ細かくする必要があります。通常、アサーションが少ないほど、テストで特定の機能をターゲットにする可能性が高くなり、同じテストで複数の機能のテストを混在させることはありません。これは、すべてのテストにアサートが 1 つしかないということですか? いいえ、しかし、同じ単体テストで複数のことをテストする可能性のあるいくつかのアサートを見つけた場合、それは「テストのにおい」と見なします。この「におい」をコードのにおいと同じように扱い、テストをリファクタリングして改良し、1 つの「もの」のみをテストするようにします (複数のアサートが必要な場合でも)。
たとえば、私は現在 MVC プロジェクトを行っており、作成したテストの 1 つは、アクションによって正しいビューがレンダリングされることです。異なるコード パスが異なるビューになる可能性がある場合、実際にはこれらのいくつかが存在する可能性があります。これが正しいビューであると定義する方法です。結果は正しい型であり、正しい名前を持っています。これには 2 つのアサートが必要ですが、テストしているのは 1 つだけです。
var result = controller.Action() as ViewResult;
Assert.IsNotNull( result );
Assert.AreEqual( viewName, result.ViewName );
モデルで同様のことを行うかもしれませんが、ビューのチェックと同じテストでモデルが正しいかどうかはテストしません。これらはコードの動作の異なる側面であるためです。予想されるモデルまたはビューを変更でき、それを別のテストに入れることで、メソッドのその機能に関係するテストのみを変更する必要があります。
私にとって、単体テストで複数のアサートを行うことは非常に一般的です。私は通常、事前条件のアサーションと、予想される事後条件のアサーションを行います。
検討:
assert(list.isEmpty());
FetchValues(list);
assert(list.count == expectedItemCount);
AssertValuesMatch(list,expectedValues);
はい、2 つのポスト コンディションを 2 つのテストに分割できますが、FetchValues のコストによっては、テスト プロセス全体が不必要に遅くなる可能性があります。
テスト メソッドでは必ず 1 つのアサートのみを使用する必要があります。多くのアサートを使用すると、複数のことをテストしているというコードの匂いがする可能性があります。さらに、別の assert を作成する代わりに、誰かが新しい assert をテストに追加できる可能性があります。また、最初のアサーションが失敗したときに、他のアサーションがどのように完了したかをどのように理解できますか?
この記事も興味深いかもしれません: https://timetocode.wordpress.com/2016/06/01/zen-of-unit-testing/
私はそれを悪い習慣とは考えていません。単体テストでは、アサート、ファイルへのログ記録、管理者への侮辱的な SMS メッセージの送信など、何でも実行できます。
考えられる問題は、追加された複雑さがテスト中のプログラムの動作を変更する可能性があることですが、注意していればめったに起こらず、いずれにせよ発見することができます。
必要なすべてのアサートを入れます。真剣に。
私は、テストの特定の目標に至るまでのすべてのステップを主張しようとします。
それは問題ではありません。唯一重要なことは、単体テストが考えられるすべてのバグをカバーすることです。
これは「考えすぎ」の例です。