テストを一連の小規模なテストに分割します
-
28-09-2019 - |
質問
小さなテストを小規模なテストに分割できるようにしたいので、小さいテストが合格すると、大きなテストも合格することを意味します(元の大きなテストを実行する理由はありません)。通常、小さいテストでは時間が少なくなり、労力が減り、脆弱でないため、これをやりたいです。このテストの分割を堅牢な方法で実現するのに役立つテスト設計パターンまたは検証ツールがあるかどうかを知りたいと思います。
小さいテストと元のテストの間の接続が、誰かが小さなテストのセットで何かを変更すると失われることを恐れています。もう1つの恐怖は、小さなテストのセットが実際に大きなテストをカバーしていないことです。
私が目指していることの例:
//Class under test
class A {
public void setB(B b){ this.b = b; }
public Output process(Input i){
return b.process(doMyProcessing(i));
}
private InputFromA doMyProcessing(Input i){ .. }
..
}
//Another class under test
class B {
public Output process(InputFromA i){ .. }
..
}
//The Big Test
@Test
public void theBigTest(){
A systemUnderTest = createSystemUnderTest(); // <-- expect that this is expensive
Input i = createInput();
Output o = systemUnderTest.process(i); // <-- .. or expect that this is expensive
assertEquals(o, expectedOutput());
}
//The splitted tests
@PartlyDefines("theBigTest") // <-- so something like this should come from the tool..
@Test
public void smallerTest1(){
// this method is a bit too long but its just an example..
Input i = createInput();
InputFromA x = expectedInputFromA(); // this should be the same in both tests and it should be ensured somehow
Output expected = expectedOutput(); // this should be the same in both tests and it should be ensured somehow
B b = mock(B.class);
when(b.process(x)).thenReturn(expected);
A classUnderTest = createInstanceOfClassA();
classUnderTest.setB(b);
Output o = classUnderTest.process(i);
assertEquals(o, expected);
verify(b).process(x);
verifyNoMoreInteractions(b);
}
@PartlyDefines("theBigTest") // <-- so something like this should come from the tool..
@Test
public void smallerTest2(){
InputFromA x = expectedInputFromA(); // this should be the same in both tests and it should be ensured somehow
Output expected = expectedOutput(); // this should be the same in both tests and it should be ensured somehow
B classUnderTest = createInstanceOfClassB();
Output o = classUnderTest.process(x);
assertEquals(o, expected);
}
解決
私が行う最初の提案は、赤であなたのテストを再導入することです(失敗)。そのためには、生産コードを一時的に破る必要があります。これにより、テストがまだ有効であることがわかります。
一般的なパターンの1つは、「大きな」テストのコレクションごとに個別のテストフィクスチャを使用することです。 「1つのテストクラスで1つのクラスのすべてのテスト」パターンに固執する必要はありません。 AAのテストセットが互いに関連しているが、別のテストセットとは無関係である場合は、自分のクラスに入れます。
個別のクラスを使用して個々の小さなテストをビッグテストのために保持することの最大の利点は、セットアップと裂け目の方法を活用できることです。あなたの場合、私はあなたがコメントした線を次のように移動します:
// this should be the same in both tests and it should be ensured somehow
セットアップ方法(junitで、注釈が付けられた方法 @Before
)。実行する必要がある異常に高価なセットアップがある場合、ほとんどのXunitテストフレームワークには、すべてのテストの前に1回実行されるセットアップメソッドを定義する方法があります。 Junitでは、これはaです public static void
を持っている方法 @BeforeClass
注釈。
テストデータが不変の場合、変数を定数として定義する傾向があります。
これをすべてまとめると、次のようなものがあるかもしれません。
public class TheBigTest {
// If InputFromA is immutable, it could be declared as a constant
private InputFromA x;
// If Output is immutable, it could be declared as a constant
private Output expected;
// You could use
// @BeforeClass public static void setupExpectations()
// instead if it is very expensive to setup the data
@Before
public void setUpExpectations() throws Exception {
x = expectedInputFromA();
expected = expectedOutput();
}
@Test
public void smallerTest1(){
// this method is a bit too long but its just an example..
Input i = createInput();
B b = mock(B.class);
when(b.process(x)).thenReturn(expected);
A classUnderTest = createInstanceOfClassA();
classUnderTest.setB(b);
Output o = classUnderTest.process(i);
assertEquals(o, expected);
verify(b).process(x);
verifyNoMoreInteractions(b);
}
@Test
public void smallerTest2(){
B classUnderTest = createInstanceOfClassB();
Output o = classUnderTest.process(x);
assertEquals(o, expected);
}
}
他のヒント
私が提案できるのは本だけです Xunitテストパターン. 。解決策がある場合は、そこにあるはずです。
theBigTest
依存関係がありません B
. 。また smallerTest1
模擬 B
依存。の smallerTest2
ock笑する必要があります InputFromA
.
なぜあなたのように依存関係グラフを作成したのですか?
A
を取る B
その後 A::process
Input
, 、その後、プロセスを投稿します InputFromA
の B
.
大きなテストとリファクタリングを維持します A
と B
依存関係マッピングを変更します。
編集 発言に応じて。
@mkorpela、私のポイントは、コードとその依存関係を見ることで、より小さなテストを作成する方法のアイデアを得る方法であるということです。 A
に依存しています B
. 。それがそれを完了するために process()
使用する必要があります B
's process()
. 。このため、 B
に依存しています A
.