将测试拆分为一组较小的测试

mko*_*ela 5 testing automated-tests unit-testing formal-verification formal-methods

我希望能够将一个大型测试分成较小的测试,以便当较小的测试通过时,它们意味着大测试也会通过(因此没有理由进行原始的大测试).我想这样做,因为较小的测试通常花费更少的时间,更少的努力和更少的脆弱.我想知道是否有测试设计模式或验证工具可以帮助我以稳健的方式实现这个测试分裂.

我担心当有人在一组较小的测试中改变某些东西时,较小的测试和原始测试之间的连接就会丢失.另一个担心是,一组较小的测试并没有真正涵盖大考验.

我的目标是一个例子:

//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);
}
Run Code Online (Sandbox Code Playgroud)

Jim*_*rne 2

我提出的第一个建议是重新考虑红色(失败)的测试。为此,您必须暂时破坏生产代码。这样,您就知道测试仍然有效。

一种常见的模式是对每个“大”测试集合使用单独的测试夹具。您不必坚持“一个测试类中一类的所有测试”模式。如果一组测试彼此相关,但与另一组测试无关,则将它们放在自己的类中。

使用单独的类来保存大测试的各个小测试的最大优点是您可以利用设置和拆卸方法。就您而言,我会移动您评论过的行:

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

设置方法(在 JUnit 中,用 注释的方法@Before)。如果您需要完成一些异常昂贵的设置,大多数 xUnit 测试框架都有一种方法来定义在所有测试之前运行一次的设置方法。在 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);
    }

}
Run Code Online (Sandbox Code Playgroud)