5

大きなテストを小さなテストに分割して、小さなテストが合格したときに大きなテストも合格することを意味するようにしたいと考えています (したがって、元の大きなテストを実行する理由はありません)。小規模なテストは通常​​、時間も労力もかからず、脆弱性も少ないため、これを行いたいと考えています。このテスト分割を堅牢な方法で実現するのに役立つテスト設計パターンまたは検証ツールがあるかどうかを知りたいです。

誰かが小さなテストのセットで何かを変更すると、小さなテストと元のテストの間の接続が失われるのではないかと心配しています。もう 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);
}
4

3 に答える 3

2

私が行う最初の提案は、テストを赤(失敗)でリファクタリングすることです。そのためには、本番コードを一時的に中断する必要があります。このようにして、テストがまだ有効であることがわかります。

一般的なパターンの1つは、「大きな」テストのコレクションごとに個別のテストフィクスチャを使用することです。「1つのテストクラスの1つのクラスのすべてのテスト」パターンに固執する必要はありません。一連のテストが互いに関連しているが、別の一連のテストとは無関係である場合は、それらを独自のクラスに入れます。

個別のクラスを使用して大きなテストの個々の小さなテストを保持する最大の利点は、セットアップメソッドとティアダウンメソッドを利用できることです。あなたの場合、私はあなたがコメントした行を移動します:

// this should be the same in both tests and it should be ensured somehow

セットアップメソッド(JUnitでは、で注釈が付けられたメソッド@Before)に。実行する必要のある非常に高価なセットアップがある場合、ほとんどのxUnitテストフレームワークには、すべてのテストの前に1回実行されるセットアップメソッドを定義する方法があります。JUnitでは、これはアノテーション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);
    }

}
于 2010-04-20T02:13:39.197 に答える
0

私が提案できるのは、本xUnit Test Patternsだけです。解決策がある場合は、そこにあるはずです。

于 2010-04-19T19:22:58.700 に答える
0

theBigTestへの依存関係がありませんB。また、依存関係smallerTest1をモックします。BあなたはsmallerTest2モックする必要がありますInputFromA

なぜあなたのように依存関係グラフを作成したのですか?

ABその後、A::process Inputで後処理を行いInputFromAますB

大きなテストとリファクタリングAを維持しB、依存関係のマッピングを変更します。

[編集]発言に応じて。

@mkorpela、私のポイントは、コードとその依存関係を見ることで、より小さなテストを作成する方法のアイデアを得る方法です。Aに依存していBます。それを完了するためには、のをprocess()使用する必要があります。このため、に依存しています。Bprocess()BA

于 2010-04-20T05:38:21.667 に答える