次のようなテストスイートを考えてみます。
public class MyTestSuite {
@Test
public void test_something() {
testHelperMethod("value11", "value12", "value13");
}
@Test
public void test_something_else_meaningful_name() {
testHelperMethod("othervalue21", "othervalue22", "othervalue23");
}
// ...
private void testHelperMethod(String value1, String value2, String value3) {
// set up part (independent of value1, value2, value3)
// action part (dependent on values)
// assertion part (dependent on values)
// tear down part (independent of values)
}
}
つまり、すべてのテストケースは、arrange-act-assertスキーマに従って構造化された単一のパラメーター化されたヘルパーメソッドを介して実行されます。テストケースでは、正確にテストする必要があるものに応じて、さまざまなパラメーターを指定してこのメソッドを呼び出すだけです。
質問:このようなテストを構造化することの欠点は、もしあれば、何ですか?これは(アンチ?)パターンに共通の名前がありますか?
備考
これは question が私が見つけた最も近いものですが、そこでの答えは他の側面/問題、つまり次のことを扱っています
より正確に言うと、testHelperMethod
のアサーション部分も個別のユーティリティメソッド(多数のパラメーターで呼び出されます)ですが、問題にはあまり関係ないと思います。 (つまり、ヘルパーメソッドにテストを委任するのが悪いとされるのはなぜですか?)
これが問題になる場合、これは統合テストですが、答えはユニットテストでも同じだと思います。
[〜#〜] edit [〜#〜]:混乱を避けるためにテストケースの名前を変更しました(以前は、test1
、test2
と呼ばれていました。実際、問題のプロジェクトのテストには意味のある名前が付けられています。私はこの単純化を自分で行ったところです。)
他の人がすでに述べたように:重複を避けるために別のメソッドを作成することは何の問題もありません。
ただし、他の人も述べたように、個々のテストで、何が設定され、何がアサートされているかを明確に示す必要があります。
テスト名はこれに非常に役立ち、実際のコードにはTest1
やTest2
よりも意味のある名前があると思います。
個々のテストのコードをより「わかりやすく」するには、つまり、そのテストのコードを読み取るだけで各テストが何を行っているかを明確にするには、ヘルパーメソッドを明白な部分に分割します。
はい、1つのヘルパーで複数の呼び出しを実行できるようになったときに、1つのヘルパーに限定されていたものを「複製」しますが、各ヘルパーメソッドにより意味のある名前を付けることができます。
// Arrange
someHelper = MakeSomeDependency(arg1, arg2, arg3);
sut = MakeSomeObjectUnderTest(someHelper);
// Act
sut.DoSomething();
// Assert
AssertCommonAsserts(sut, expectedvalue1, expectedvalue2);
Assert.AreEqual(sut.SomeProperty, expectedValue);
注:私は単一のテストで複数のアサートのファンではありませんが、単一の「ファクト」は複数のアサートによってのみチェックできる場合があり、「ガード」アサートがあるいくつかのシナリオを考えることもできますアサートされる実際の「事実」が失敗のデバッグに役立つ前に。
名前付きパラメーターをサポートする言語を使用している場合は、その機能を使用してテストをさらにわかりやすくすることができます。これにより、メソッド呼び出しは、使用される値だけでなく、それらの値の用途も伝えます。
// Arrange
someHelper = MakeSomeDependency(
knownNames: arg1,
unknownNames: arg2,
lookingFor: arg3);
sut = MakeSomeObjectUnderTest(someHelper);
// Act
sut.DoSomething();
// Assert
AssertCommonAsserts(
objectUnderTest: sut,
numberOfItemsFound: expectedvalue1,
nameOfItemFound: expectedvalue2);
Assert.AreEqual(sut.SomeProperty, expectedValue);
テストコードとビジネスコードの構造に問題はありません。テストスイートのポイントは、テストされたコードがどのように動作するかを明確に表現し、動作しない場合はエラーを通知することです。テストコードに賢明なリファクタリングと適切に選択された名前を使用すると、カットアンドペーストの煩わしさよりもこの目標を提供できます。
(ただし、Matthewは完全に正しい:allテストに共通するセットアップおよびティアダウンコードは、カスタムサブルーチンではなく、フレームワークの特別なメソッドに属しています。)
上位のアプローチではなく、特定のテストケースのすべての有効な入力にDataProviderを使用するパラメーター化されたランナーの使用を検討する必要があります。ヘルパーメソッドを使用するのは、テストの設定が複雑でテストの配置が煩雑になる場合に、テストを読みやすくするためだけです。
それはどのように見えるかもしれません:
@RunWith( DataProviderRunner.class )
public class YourClassTest {
@Test
@DataProvider( value = { "value11|value12|value13","value21|value22|value23" },
trimValues = true, splitBy = "\\|" )
public void test_something(String value1, String value2, String value3) {
// action part (dependent on values)
// assertion part (dependent on values)
}
@Before
public void independendSetup() {
// set up part (independent of value1, value2, value3)
}
@After
public void independendtearDown() {
// tear down part (independent of values)
}
}
単体テストでは、一般的なコードを完全に除外する必要があります。乱雑で構造が不適切な単体テストコードは、テストするコードがどれほど美しいかに関係なく、理解しにくく、維持するのが難しいテストにつながります。
つまり、この特定のケースでは、.NETテストフレームワークNUnitを使用している場合、これはまったく必要ありません。ヘルパーをテストメソッドに変換し、TestCase
属性を使用してフレームワークを取得します。異なるパラメータセットで複数回呼び出す。 Javaと他の言語で利用できる同等のものがなかったとしたら、私は驚きます。
ユニットテストを適切に構造化するだけでなく、テストフレームワークが役立つ可能性があるため、テストフレームワークの機能を確実に学習してください。