52

Mockito 似乎是一个非常漂亮的 Java 存根/模拟框架。唯一的问题是我找不到任何关于使用他们的 API 的最佳方式的具体文档。测试中常用的方法包括:

doXXX(???) : Stubber
when(T) : OngoingStubbing
then(T) : OngoingStubbing
verify(???) : T
given(T) : BDDOngoingStubbing
willXXX(???) : BDDStubber

当您在实践中看到 Mockito 的示例时,您会看到如下代码:

when(yourMethod()).thenReturn(5);

从我读过的所有文档中,我已经确定了 Mockito “语法”的几个“模式”,这些模式是通过将这些方法调用以菊花链方式连接在一起的,就像上面的示例一样。我发现的一些常见模式是:

何时/然后: when(yourMethod()).thenReturn(5);

给定/将:给定(yourMethod()).willThrow(OutOfMemoryException.class);

做/当: doReturn(7).when(yourMock.fizzBu​​zz());

Will/Given/Do: willReturn(any()).given(yourMethod()).doNothing();

验证/执行: verify(yourMethod()).doThrow(SomeException.class);

我正在窒息的是如何选择正确的方法调用模式/组合来为我的测试用例建模。似乎您可以将它们以看似无穷无尽的组合菊花链式连接在一起,我不确定哪种模式适合哪个问题。

一些 Mockito Guru 能否帮助阐明 Mockito 方法的哪些模式/组合用于哪些类型的测试用例(以及为什么)?提前致谢!

4

3 回答 3

40

when/thenReturnwhen/thenThrow语法有几个缺点when/then。例如,

  • 在 的情况下when/thenReturn,如果返回类型是带有通配符的泛型,并且您希望返回相同类型的模拟,您将无法避免编译警告。
  • 您不能将when/thenThrowandwhen/then用于 void 方法。
  • 您不能在 Mockito 间谍上使用这些语法。
  • 模拟对象、方法和参数的每个组合只能调用when一次,除非reset在模拟上调用。
  • when当您使用参数匹配器时,为模拟对象和方法的一种组合多次调用可能会导致问题。

我发现这些案例很难记住。因此,与其尝试跟踪 when/thenReturn,when/thenThrowwhen/then语法何时会起作用和不会起作用,我更愿意完全避免它们,而支持doReturn/when,doThrow/whendoAnswer/when替代方案。也就是说,由于你偶尔会需要doReturn/when,doThrow/whendoAnswer/when, 并且你总是可以使用这些方法,所以学习如何使用when/thenReturn,when/thenThrow和是没有意义的when/then

请注意doReturndoThrow和可以以与和doAnswer相同的方式链接thenReturn在一起。他们没有的是在一次调用中返回多个值(或抛出多个异常,或运行多个答案)的选项,和。但我发现我很少需要这样做,这并不重要。thenThrowthendoReturndoThrowdoAnswer

的还有一个缺点doReturn,我认为这无关紧要。您不会像使用when/thenReturn. 所以如果你弄错了参数类型,在你运行测试之前你不会发现。坦白说,我不在乎。

综上所述,我使用 Mockito 已经两年多了,我认为一致使用doReturn,doThrowdoAnswer是 Mockito 的最佳实践。其他 Mockito 用户不同意。

于 2012-07-14T01:02:51.800 回答
18

Mockito 通常有几种做事方式。

我发现自己主要使用:

// Setup expectations
when(object.method()).thenReturn(value);
when(object.method()).thenThrow(exception);
doThrow(exception).when(object.voidMethod());


// verify things
verify(object, times(2)).method();
verify(object, times(1)).voidMethod();

我发现我可以通过这三种调用完成 95% 的操作。

另外,您使用的是什么版本的 Mockito?最新版本(1.9.0+)中不存在“given”和“will”结构

但是,在某些情况下,我希望返回值或异常响应输入。在这种情况下,您可以使用 Answer 接口检查方法参数并返回适当的值。

public class ReturnFirstArg<T> implements Answer<T> {
    public T answer(InvocationOnMock invocation) {
        return invocation.getArguments()[0];
    }
}

when(object.method(7)).thenAnswer(new ReturnFirstArg<Integer>());
于 2012-07-13T01:54:19.197 回答
10

事实上,事情看起来比你想象的要简单得多

参考:http ://static.javadoc.io/org.mockito/mockito-core/2.7.12/org/mockito/Mockito.html

验证

为了使用 Mockito,您需要了解 Mockito 的一个基本理念:Stubbing 和 Verification 是分离的。因此,您提到的“验证/执行”实际上是在做“验证”工作,而其他 4 个“语法”是用于存根的。存根定义了模拟对象在不同情况下的反应方式。验证是为了确保在之前对被测系统 (SUT) 的调用中按预期调用了哪些模拟。

何时/然后,给定/将

然后是“When”和“Given”系列。您可以简单地将它们视为彼此的别名。Mockito 1.8.x 中添加了“Given”系列,使其看起来更符合 BDD 实践。

DoXxx :

在正常情况下,我们主要使用when(xxx).then(...)(and given(...).will(...)) 。但是,在某些情况下,语法不起作用。最明显的情况是存根方法的返回类型为 void。在这种情况下when(mockObj.voidMethod()).thenThrow(anException)不会编译。作为一种解决方法,创建了 Do/When 的替代语法,因此您可以将上一行写为doThrow(anException).when(mockObj.voidMethod())

于 2012-07-13T04:18:40.893 回答