5

我将以我非常年轻并且没有太多测试经验并且以前从未使用过模拟框架的背景作为开场白。

我正在为涉及许多不同 Web 服务的代码编写单元测试,我对此进行了嘲笑。我的许多测试都验证了调用的结果,其中我的所有服务调用都成功,但对 ServiceX 的调用除外。我的第一直觉是为 @Before 块中的所有模拟设置快乐路径行为,然后为每个测试修改模拟行为。

@Before
public void init(){
    when(serviceA.doSomething()).thenReturn(true);
    when(serviceB.doSomething()).thenReturn(true);
    when(serviceC.doSomething()).thenReturn(true);
    when(serviceD.doSomething()).thenReturn(true);
    when(serviceE.doSomething()).thenReturn(true);
}

@Test
public void testDoBusinessSuccess(){
    String result = businessLogic.doBusiness();
    assertThat(result, is("success"));
}

@Test
public void testDoBusinessFailureWhenServiceAFails(){
    when(serviceA.doSomething()).thenReturn(false);

    String result = businessLogic.doBusiness();
    assertThat(result, is("service A is down!"));
}

@Test
public void testDoBusinessFailureWhenServiceBFails(){
    when(serviceB.doSomething()).thenReturn(false);

...

这使得每个测试用例都简洁,并且很容易看到正在测试的内容,因为我只指定了偏离规范的行为。

但我怀疑这不是 Mockito 希望我设置模拟行为的方式,因为当我尝试验证 ServiceB 中的失败意味着 ServiceC 永远不会被命中时,我意识到我when(serviceC.doSomething())在 @Before 中的调用算作对 serviceC 的调用. 也就是说,我verifyZeroInteractions(serviceC)总是失败,因为我调用了when(serviceC.doSomething()).thenReturn(true),即使测试用例从未触及 serviceC 。

那么最佳实践是什么?即使我会在所有地方重复几乎相同的 5 行,我是否最好在每次测试中明确设置每个模拟的行为?

4

3 回答 3

2
when(serviceC.doSomething()) in the @Before counted as invocations on serviceC

我怀疑该when构造是否被视为调用,因为这只是存根。
您能否仔细检查您的代码以查看是否serviceC.doSomething从您的内部调用@Test

关于最佳实践,我认为您应该只将所有测试用例常见的存根行为转移到@Before

除了您的代码看起来的方式之外,我认为您可以尝试重构为Chain of Responsibility模式,应该有助于您编写测试代码。

于 2012-10-25T04:42:21.020 回答
0

这并不特定于模拟,但只有每个测试共有的代码才应该放在 @Before 中,因为它会在每个 junit 测试之前执行。

因此,如果您想要特定于测试的行为,那么所有那些嘲笑的东西都应该在这些测试中。

如果您有多个测试共有的行为,您可以将其提取到一个方法中,该方法仅由需要该行为的测试调用。

于 2012-10-25T04:39:15.470 回答
0

正在计数的调用verify是测试方法本身中的调用。将 each 方法中的调用更改为如下所示。

doReturn(false).when(serviceA).doSomething();

这将与您正在做的事情在被存根方面产生相同的效果;doSomething但出于验证的目的,它不会被视为调用。

于 2012-10-25T16:36:34.063 回答