Difference Between when() and doXxx() Methods in Mockito – Mockito中when()和doXxx()方法的区别

最后修改: 2020年 9月 8日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

Mockito is a popular Java mocking framework. With it, it’s simple to create mock objects, configure mock behavior, capture method arguments, and verify interactions with mocks.

Mockito是一个流行的Java嘲弄框架。使用它,可以简单地创建模拟对象配置模拟行为捕获方法参数,以及验证与模拟的交互关系

Now, we’ll focus on specifying mock behavior. We have two ways to do that: the when().thenDoSomething() and the doSomething().when() syntax.

现在,我们将专注于指定模拟行为。我们有两种方法:when().thenDoSomething()doSomething().when()语法。

In this short tutorial, we’ll see why we have both of them.

在这个简短的教程中,我们将看到为什么我们有这两样东西。

2. when() Method

2.when()方法

Let’s consider the following Employee interface:

让我们考虑以下雇员接口。

interface Employee {
    String greet();
    void work(DayOfWeek day);
}

In our tests, we use a mock of this interface. Let’s say we want to configure the mock’s greet() method to return the string “Hello”. It’s straightforward to do so using Mockito’s when() method:

在我们的测试中,我们使用这个接口的一个模拟。假设我们想配置这个模拟的greet()方法来返回字符串“Hello”。使用Mockito的when()方法可以很直接地做到这一点。

@Test
void givenNonVoidMethod_callingWhen_shouldConfigureBehavior() {
    // given
    when(employee.greet()).thenReturn("Hello");

    // when
    String greeting = employee.greet();

    // then
    assertThat(greeting, is("Hello"));
}

What happens? The employee object is a mock. When we call any of its methods, Mockito registers that call. With the call of the when() method, Mockito knows that this invocation wasn’t an interaction by the business logic. It was a statement that we want to assign some behavior to the mock object. After that, with one of the thenXxx() methods, we specify the expected behavior.

会发生什么?employee对象是一个mock。当我们调用它的任何方法时,Mockito会注册该调用。通过对when()方法的调用,Mockito知道这个调用不是业务逻辑的交互。它是一个声明,我们想给模拟对象分配一些行为。之后,通过一个thenXxx()方法,我们指定了预期行为。

Until this point, it’s good old mocking. Likewise, we want to configure the work() method to throw an exception, when we call it with an argument of Sunday:

在这之前,这是很好的老式嘲弄。同样地,我们想配置work()方法,使其在调用参数为Sunday时抛出一个异常。

@Test
void givenVoidMethod_callingWhen_wontCompile() {
    // given
    when(employee.work(DayOfWeek.SUNDAY)).thenThrow(new IAmOnHolidayException());

    // when
    Executable workCall = () -> employee.work(DayOfWeek.SUNDAY);

    // then
    assertThrows(IAmOnHolidayException.class, workCall);
}

Unfortunately, this code won’t compile, because in the work(employee.work(…)) call, the work() method has a void return type; hence we cannot wrap it into another method call. Does it mean that we can’t mock void methods? Of course, we can. doXxx methods to the rescue!

不幸的是,这段代码无法编译,因为在work(employee.work(…))调用中,work()方法有一个void返回类型;因此我们无法将其封装到另一个方法调用中。这是否意味着我们不能模拟void方法?当然,我们可以。doXxx方法来拯救我们!

3. doXxx() Methods

3.doXxx()方法

Let’s see how we can configure the exception throwing with the doThrow() method:

让我们看看如何用doThrow()方法来配置异常的抛出。

@Test
void givenVoidMethod_callingDoThrow_shouldConfigureBehavior() {
    // given
    doThrow(new IAmOnHolidayException()).when(employee).work(DayOfWeek.SUNDAY);

    // when
    Executable workCall = () -> employee.work(DayOfWeek.SUNDAY);

    // then
    assertThrows(IAmOnHolidayException.class, workCall);
}

This syntax is slightly different than the previous one: we don’t try to wrap a void method call inside another method call. Therefore, this code compiles.

这个语法与之前的语法略有不同:我们没有尝试将一个void方法调用包裹在另一个方法调用中。因此,这段代码可以编译。

Let’s see what just happened. First, we stated that we want to throw an exception. Next, we called the when() method, and we passed the mock object. After that, we specified which mock interaction’s behavior we want to configure.

让我们看看刚才发生了什么。首先,我们声明我们要抛出一个异常。接下来,我们调用了when()方法,并传递了模拟对象。之后,我们指定了我们想要配置的模拟交互的行为。

Note that this isn’t the same when() method we used before. Also, note that we chained the mock interaction after the invocation of when(). Meanwhile, we defined it inside the parentheses with the first syntax.

请注意,这并不是我们之前使用的那个when()方法。另外,请注意我们在调用when()之后链上了模拟交互。同时,我们在括号内用第一个语法定义了它。

Why do we have the first when().thenXxx(), when it isn’t capable of such a common task, as configuring a void invocation? It has multiple advantages to the doXxx().when() syntax.

为什么我们有第一个when().thenXxx(),而它并不能够完成这样一个普通的任务,如配置一个void调用?它比doXxx().when()语法有多种优势。

First, it’s more logical for developers to write and read statements like “when some interaction, then do something” than “do something, when some interaction”.

首先,对于开发人员来说,编写和阅读 “当某种互动时,就做某事 “这样的语句比 “做某事,当某种互动 “更符合逻辑。

Second, we can add multiple behaviors to the same interaction with chaining. That’s because when() returns an instance of the class OngoingStubbing<T>, which’s thenXxx() methods return the same type.

第二,我们可以通过链式添加多个行为到同一个交互中。这是因为when()返回OngoingStubbing<T>类的实例,其thenXxx()方法返回同一类型。

On the other hand, doXxx() methods return a Stubber instance, and Stubber.when(T mock) returns T, so we can specify what kind of method invocation we want to configure. But T is part of our application, for example, Employee in our code snippets. But T won’t return a Mockito class, so we won’t be able to add multiple behaviors with chaining.

另一方面,doXxx()方法返回一个Stubber实例,而Stubber.when(T mock)返回T,所以我们可以指定我们想要配置的方法调用的种类。但是T是我们应用程序的一部分,例如,在我们的代码片断中的Employee。但是T不会返回一个Mockito类,所以我们将不能用链式添加多个行为。

4. BDDMockito

4.BDDMockito

BDDMockito uses an alternative syntax to those which we covered. It’s pretty simple: in our mock configurations, we have to replace the keyword “when” to “given” and the keyword “do” to “will“. Other than that, our code remains the same:

BDDMockito使用了一种与我们所介绍的语法不同的语法。这很简单:在我们的模拟配置中,我们必须将关键字”when”替换为”given“,将关键字”do“替换为”will“。除此以外,我们的代码保持不变。

@Test
void givenNonVoidMethod_callingGiven_shouldConfigureBehavior() {
    // given
    given(employee.greet()).willReturn("Hello");

    // when
    String greeting = employee.greet();

    // then
    assertThat(greeting, is("Hello"));
}

@Test
void givenVoidMethod_callingWillThrow_shouldConfigureBehavior() {
    // given
    willThrow(new IAmOnHolidayException()).given(employee).work(DayOfWeek.SUNDAY);

    // when
    Executable workCall = () -> employee.work(DayOfWeek.SUNDAY);

    // then
    assertThrows(IAmOnHolidayException.class, workCall);
}

5. Conclusion

5.总结

We saw the advantages and disadvantages of the configuring a mock object the when().thenXxx() or the doXxx().when() way. Also, we saw how these syntaxes work and why we have both.

我们看到了用when().thenXxx()doXxx().when()方式配置模拟对象的优点和缺点。此外,我们还看到了这些语法是如何工作的,以及为什么我们有这两种语法。

As usual, the examples are available over on GitHub.

像往常一样,这些例子可以在GitHub上找到over