web-dev-qa-db-ja.com

単体テストで複数のアサーションを使用するのは悪い習慣ですか?

単体テストで複数のアサーションを使用するのは悪い習慣ですか?それは重要ですか?

44
leora

テストケースごとに1つのassertがある場合もありますが、多くの場合、複数のassertステートメントがあると思います。

@Arkainがとらえどころのないケースを見てきました。この場合、非常に大きなコードには、いくつかのテストケースを含む単一のユニットテストスイートがあり、それらはすべてtestCase1testCase2などのラベルが付けられています。 、および各テストケースには数百のアサーションがあります。さらに良いことに、各条件は通常、以前の実行の副作用に依存します。ビルドが失敗するたびに、常にそのような単体テストでは、問題がどこにあるかを判断するのにかなりの時間がかかります。

しかし、もう1つの極端な例は、あなたの質問が示唆していることです。考えられる条件ごとに個別のテストケースです。テストする内容によっては、これは理にかなっているかもしれませんが、テストケースごとにいくつかのassertsがあることがよくあります。

たとえば、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を持たないでください。
  • 同じ機能/機能またはそのファセットをテストするassertsをグループ化します。必要がない場合は、複数の単体テストケースのオーバーヘッドは必要ありません。
  • 上記のルールはいずれも、実用性と常識によって上書きする必要があります。おそらく、それぞれに1つのアサーション(または複数のアサーション)を持つ1000個の単体テストケースは必要ありません。また、数百のassertステートメントを持つ単一のテストケースは必要ありません。
42
Jared Oberhaus

いいえ、それは悪い習慣ではありません。テストしているメソッドがクラスを返す場合は、設定されているはずのさまざまな変数をテストする必要があります。この目的のために、1つの単体テストを使用することもできます。

ただし、1つの単体テストで複数の機能をテストしている場合、どの機能が問題の原因であるかがいつ失敗するかは明確ではありません。ユニットテストはあなたの友達であることを忘れないでください、それで彼らにあなたを助けさせてください。何が悪かったのかを簡単に確認できるようにして、修正できるようにします。

17

ユニットテストは、適度にきめ細かくする必要があります。通常、アサートが少ないほど、テストが特定の機能を対象とし、同じテストで複数の機能のテストを混在させない可能性が高くなります。これは、すべてのテストに1つのアサーションのみが必要であることを意味しますか?いいえ。ただし、複数のアサーションが見つかった場合は「テスト臭」と見なし、同じ単体テストで複数のものをテストする可能性があります。この「匂い」をコードの臭いと同じように扱い、テストをリファクタリングして、複数のアサーションが必要な場合でも、1つの「もの」だけをテストするようにテストを調整します。

たとえば、私は現在MVCプロジェクトを実行しており、私が作成するテストの1つは、アクションによって正しいビューがレンダリングされることです。異なるコードパスが異なるビューをもたらす可能性がある場合、実際にはこれらのいくつかが存在する可能性があります。これが私がそれを正しいビューであると定義する方法です:結果は正しいタイプであり、正しい名前を持っています。これには2つのアサーションが必要ですが、テストしているのは1つだけです。

var result = controller.Action() as ViewResult;

Assert.IsNotNull( result );
Assert.AreEqual( viewName, result.ViewName );

モデルで同様のことをするかもしれませんが、これらはコードの動作の異なる側面であるため、ビューをチェックするのと同じテストでモデルが正しいことをテストしません。期待されるモデルまたはビューを変更することができ、それを別のテストに入れることで、メソッドのその機能に関係するテストのみを変更する必要があります。

10
tvanfosson

私にとって、単体テストで複数のアサートを行うことは非常に一般的です。私は通常、前提条件のアサーションと、予想される事後条件のアサーションを持っています。

考えてみましょう:

assert(list.isEmpty());

FetchValues(list);

assert(list.count == expectedItemCount);

AssertValuesMatch(list,expectedValues);

はい、2つの事後条件を2つのテストに分割することはできますが、FetchValuesのコストによっては、テストプロセス全体が不必要に遅くなる可能性があります。

6
MikeJ

私はそれを悪い習慣とは考えていません。単体テストでは、アサート、ファイルへのログ記録、侮辱的なSMSメッセージの管理者への送信など)を行うことができます。

possibleの問題は、複雑さが増すとテスト対象のプログラムの動作が変わる可能性があることですが、注意している場合はめったにありません。とにかく発見できます。

2
paxdiablo

テストメソッドでは間違いなく1つのアサーションのみを使用する必要があります!多くのアサーションを使用することは、複数のことをテストしているというコードの臭いである可能性があります。さらに、誰かが別のアサーションを作成する代わりに、新しいアサーションをテストに追加できる可能性があります。そして、最初のアサーションが失敗したときに、他のアサーションがどのように完了したかをどのように理解できますか?

この記事もおもしろいと思うかもしれません: https://timetocode.wordpress.com/2016/06/01/zen-of-unit-testing/

1
Mutex

必要なすべてのアサートを入力します。真剣に。

私は、テストの特定の目標に至るまでのすべてのステップを主張しようとしています。

1
plinth

「ユニットテスト」は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");

}
0
Peter Gfader

それは問題ではありません。重要なのは、ユニットテストで考えられるすべてのバグをカバーすることだけです。

これは「考えすぎ」の一例です。

0
user88637