単体テストで複数のアサーションを使用するのは悪い習慣ですか?それは重要ですか?
テストケースごとに1つのassert
がある場合もありますが、多くの場合、複数のassert
ステートメントがあると思います。
@Arkainがとらえどころのないケースを見てきました。この場合、非常に大きなコードには、いくつかのテストケースを含む単一のユニットテストスイートがあり、それらはすべてtestCase1
、testCase2
などのラベルが付けられています。 、および各テストケースには数百のアサーションがあります。さらに良いことに、各条件は通常、以前の実行の副作用に依存します。ビルドが失敗するたびに、常にそのような単体テストでは、問題がどこにあるかを判断するのにかなりの時間がかかります。
しかし、もう1つの極端な例は、あなたの質問が示唆していることです。考えられる条件ごとに個別のテストケースです。テストする内容によっては、これは理にかなっているかもしれませんが、テストケースごとにいくつかのassert
sがあることがよくあります。
たとえば、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
sをグループ化します。必要がない場合は、複数の単体テストケースのオーバーヘッドは必要ありません。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のコストによっては、テストプロセス全体が不必要に遅くなる可能性があります。
私はそれを悪い習慣とは考えていません。単体テストでは、アサート、ファイルへのログ記録、侮辱的なSMSメッセージの管理者への送信など)を行うことができます。
possibleの問題は、複雑さが増すとテスト対象のプログラムの動作が変わる可能性があることですが、注意している場合はめったにありません。とにかく発見できます。
テストメソッドでは間違いなく1つのアサーションのみを使用する必要があります!多くのアサーションを使用することは、複数のことをテストしているというコードの臭いである可能性があります。さらに、誰かが別のアサーションを作成する代わりに、新しいアサーションをテストに追加できる可能性があります。そして、最初のアサーションが失敗したときに、他のアサーションがどのように完了したかをどのように理解できますか?
この記事もおもしろいと思うかもしれません: https://timetocode.wordpress.com/2016/06/01/zen-of-unit-testing/
必要なすべてのアサートを入力します。真剣に。
私は、テストの特定の目標に至るまでのすべてのステップを主張しようとしています。
「ユニットテスト」は1つのユニットをテストする必要があるため、できるだけ小さくして、特定の1つだけをテストします。アサートを1つだけにすることをお勧めします
しかし、2つのアサーションが前後にない限り、2つのアサーションがあっても問題ないと思います。
悪い例
public void ValidateRulesEntry_Valid_ValidConditionsFromFile()
{
string condition = "Target.HasValue";
string returnMessage;
bool successFul = CodeParserTryParseCondition(condition, out returnMessage);
Assert.IsTrue(successFul);
Assert.IsFalse(string.IsNullOrEmpty(returnMessage));
Assert.IsTrue(returnMessage == "OK");
}
それは問題ではありません。重要なのは、ユニットテストで考えられるすべてのバグをカバーすることだけです。
これは「考えすぎ」の一例です。