Mockito的mock方法

2023/05/12

1. 概述

在本教程中,我们将介绍Mockito API中标准静态方法mock()的各种用途。

下面的MyList类将被用作测试用例中要mock的collaborator(协作者):

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, is(false));

3. 指定名称的mock

在本节中,我们介绍mock()方法的另一种重载,它提供了一个指定mock名称的参数:

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

一般来说,mock的名称与代码的执行无关。但是,在调试时它可能会有所帮助,因为我们可以使用mock的名称来定位验证错误。

可以确保验证失败引发的异常消息包含提供的mock名称。

在下面的代码中,我们将为MyList类创建一个mock并将其命名为myMock:

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

然后我们配置mock上的方法交互,并调用它:

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

这里我们故意创建一个失败的验证,该验证应该引发异常,并带有一条包含有关mock信息的消息:

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

以下是抛出的异常的消息:

org.mockito.exceptions.verification.TooFewActualInvocations: 
myMock.add(<any string>);
Wanted 2 times:
-> at java.base/java.util.AbstractList.add(AbstractList.java:111)
But was 1 time:
-> at cn.tuyucheng.taketoday.mockito.MockitoMockUnitTest.whenUsingMockWithName_thenCorrect(MockitoMockUnitTest.java:36)

如我们所见,异常消息包含mock的名称,这对于在验证不成功的情况下定位错误很有用。

4. 使用Answer mock

在这里,我们演示另一个mock()重载的使用,其中我们将为mock在创建时对交互的Answer配置策略。 Mockito文档中此mock()方法的定义如下所示:

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

首先我们定义一个Answer接口的实现:

private static class CustomAnswer implements Answer<Boolean> {

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

我们将使用上面的CustomAnswer类来创建mock:

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

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

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

以下验证和断言确认带有Answer参数的mock()方法按预期工作:

verify(listMock).add(anyString());
assertThat(added, is(false));

5. 使用MockSettings进行mock

最后一种mock()方法是带有MockSettings类型参数的重载。我们使用这个重载方法来提供一个非标准的mock。

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

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

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

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

我们将在创建新mock时使用该MockSettings对象:

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

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

boolean added = listMock.add(randomAlphabetic(6));
verify(listMock).add(anyString());
assertThat(added, is(false));

6. 总结

在本文中,我们详细介绍了Mockito中mock()方法各种重载的使用。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章