1. 概述

在本教程中,我们将介绍 Mockito 的静态方法 - mock的各种用法。

与本站的其他 Mockito 框架的文章一样(如 Mockito VerifyMockito When/Then,我们将使用下面的 MyList 类将作为本文的测试用例:

public class MyList extends AbstractList<String> {

    @Override
    public String get(int index) {
        return null;
    }

    @Override
    public int size() {
        return 1;
    }
}

2. 简单mock

mock 有好几个重载方法,最简单的一个只需传入被mock 的class

public static <T> T mock(Class<T> classToMock)

我们将使用此方法来mock一个类,并设置一个期望值:

MyList listMock = mock(MyList.class);
when(listMock.add(anyString())).thenReturn(false);

然后,我们在 mock 的对象上执行一个方法:

boolean added = listMock.add(randomAlphabetic(6));

下面的测试用代码,确认了我们调用了 mock 上的 add 方法。调用返回的值与我们之前设定的期望值一致:

verify(listMock).add(anyString());
assertThat(added).isFalse();

3. 指定Mock的名字

在本节中,我们将介绍 mock 的第二个重载方法,第二个参数指定了 mock 的名称:

public static <T> T mock(Class<T> classToMock, String name)

一般来说,这个名字没啥用。不过,它在调试时可能会有所帮助,因为我们会使用 mock 的名字来追踪错误。

为了确保验证不成功时抛出的异常信息中包含所提供的 mock 名称,我们将使用 assertThatThrownBy. 来进行验证。 在下面的代码中,我们将为 MyList 类创建一个 mock,并将其命名为 myMock

MyList listMock = mock(MyList.class, "myMock");

然后,我们将对 mock 的方法设置期望值并执行它:

when(listMock.add(anyString())).thenReturn(false);
listMock.add(randomAlphabetic(6));

接下来,我们将在 assertThatThrownBy 中调用验证,并验证所抛出异常的实例:

assertThatThrownBy(() -> verify(listMock, times(2)).add(anyString()))
    .isInstanceOf(TooFewActualInvocations.class)

此外,我们还可以验证异常信息是否包含有关mock的信息:

assertThatThrownBy(() -> verify(listMock, times(2)).add(anyString()))
    .isInstanceOf(TooFewActualInvocations.class)
    .hasMessageContaining("myMock.add");

下面是抛出的异常信息:

org.mockito.exceptions.verification.TooLittleActualInvocations:
myMock.add(<any>);
Wanted 2 times:
at com.baeldung.mockito.MockitoMockTest
  .whenUsingMockWithName_thenCorrect(MockitoMockTest.java:...)
but was 1 time:
at com.baeldung.mockito.MockitoMockTest
  .whenUsingMockWithName_thenCorrect(MockitoMockTest.java:...)

我们可以看到,异常消息中包含了mock的名称,这将有助于在验证不成功时找到故障点。

4. 自定义Answer

在这里,我们将演示 mock 的另一个重载方法,其中我们将在创建时配置mock对交互的answer的策略。 该方法的定义如下所示:

public static <T> T mock(Class<T> classToMock, Answer defaultAnswer)

让我们从 Answer 接口的实现定义开始:

class CustomAnswer implements Answer<Boolean> {

    @Override
    public Boolean answer(InvocationOnMock invocation) throws Throwable {
        return false;
    }
}

我们将使用上面的 CustomAnswer 类来生成mock:

MyList listMock = mock(MyList.class, new CustomAnswer());

如果我们不对方法设置期望值,CustomAnswer类型配置的默认answer就会发挥作用。为了证明这一点,我们将跳过设置期望值的步骤,直接执行方法:

boolean added = listMock.add(randomAlphabetic(6));

下面的验证和断言确认了带有 Answer 参数的 mock 方法按预期运行:

verify(listMock).add(anyString());
assertThat(added).isFalse();

5. MockSettings

本文要介绍的最后一个 mock 方法是带有 MockSettings 参数的重载方法。我们使用这个重载方法来提供一个非标准的mock。

MockSettings 接口的方法支持多种自定义设置,例如使用 invocationListeners 为当前 mock 上的方法调用注册监听器、使用 serializable 配置序列化、使用 spiedInstance 指定要监视的实例、使用 useConstructor 配置 Mockito 在实例化 mock 时尝试使用构造函数等。

为了方便起见,我们将重复使用上一节介绍的 CustomAnswer 类,创建一个 MockSettings 实现,定义默认answer。

MockSettings 对象由工厂方法实例化:

MockSettings customSettings = withSettings().defaultAnswer(new CustomAnswer());

我们将在创建新的mock时使用该设置对象:

MyList listMock = mock(MyList.class, customSettings);

与上一节类似,我们将调用 MyList 实例的 add 方法,并验证带有 MockSettings 参数的 mock 方法是否按预期运行:

boolean added = listMock.add(randomAlphabetic(6));

verify(listMock).add(anyString());
assertThat(added).isFalse();

6. 总结

在本文中,我们详细介绍了 Mockito 的 mock 方法。这些示例和代码片段的实现可以在 GitHub 中找到。