5

我希望能够将大测试拆分为较小的测试,以便当较小的测试通过时,它们暗示大测试也会通过(因此没有理由运行原始的大测试)。我想这样做是因为较小的测试通常需要更少的时间、更少的精力并且更不脆弱。我想知道是否有测试设计模式或验证工具可以帮助我以稳健的方式实现这种测试拆分。

我担心当有人更改较小测试集中的某些内容时,较小测试和原始测试之间的联系会丢失。另一个担心是较小的测试集并没有真正涵盖大的测试。

我的目标是一个例子:

//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

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

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

使用单独的类来为大测试保存单独的小测试的最大优点是您可以利用设置和拆卸方法。在您的情况下,我会移动您评论过的行:

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

到 setup 方法(在 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);
    }

}
于 2010-04-20T02:13:39.197 回答
0

我只能建议xUnit 测试模式这本书。如果有解决方案,它应该在那里。

于 2010-04-19T19:22:58.700 回答
0

theBigTest缺少对B. 还smallerTest1模拟B依赖。在smallerTest2你应该嘲笑InputFromA.

为什么要像以前一样创建依赖图?

A需要一个Bthen when A::process Input,然后你InputFromAB.

保持大测试和重构AB改变依赖映射。

[编辑]回应评论。

@mkorpela,我的观点是,通过查看代码及其依赖关系,您可以开始了解如何创建更小的测试。A依赖于B. 为了完成它process(),它必须使用B's process()。因此,B依赖于A.

于 2010-04-20T05:38:21.667 回答