作者|mghio
编辑|包包
前言
在计算机编程中,单元测试是一种软件测试方法,通过该方法可以测试源代码的各个单元功能是否适合使用。为代码编写单元测试有很多好处,包括可以及早的发现代码错误,促进更改,简化集成,方便代码重构以及许多其它功能。使用 Java 语言的朋友应该用过或者听过 Junit 就是用来做单元测试的,那么为什么我们还需要 Mockito 测试框架呢?想象一下这样的一个常见的场景,当前要测试的类依赖于其它一些类对象时,如果用 Junit 来进行单元测试的话,我们就必须手动创建出这些依赖的对象,这其实是个比较麻烦的工作,此时就可以使用 Mockito 测试框架来模拟那些依赖的类,这些被模拟的对象在测试中充当真实对象的虚拟对象或克隆对象,而且 Mockito 同时也提供了方便的测试行为验证。这样就可以让我们更多地去关注当前测试类的逻辑,而不是它所依赖的对象。
生成 Mock 对象方式
要使用 Mockito,首先需要在我们的项目中引入 Mockito 测试框架依赖,基于 Maven 构建的项目引入如下依赖即可:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.3.3</version>
<scope>test</scope>
</dependency>
如果是基于 Gradle 构建的项目,则引入如下依赖:
testCompile group: 'org.mockito', name: 'mockito-core', version: '3.3.3'
使用 Mockito 通常有两种常见的方式来创建 Mock 对象。
1、使用 Mockito.mock(clazz) 方式
通过 Mockito 类的静态方法 mock 来创建 Mock 对象,例如以下创建了一个 List 类型的 Mock 对象:
List<String> mockList = Mockito.mock(ArrayList.class);
由于 mock 方法是一个静态方法,所以通常会写成静态导入方法的方式,即 List<String> mockList = mock(ArrayList.class)。
2、使用 @Mock 注解方式
第二种方式就是使用 @Mock 注解方式来创建 Mock 对象,使用该方式创需要注意的是要在运行测试方法前使用 MockitoAnnotations.initMocks(this) 或者单元测试类上加上 @ExtendWith(MockitoExtension.class) 注解,如下所示代码创建了一个 List 类型的 Mock 对象(PS: @BeforeEach 是 Junit 5 的注解,功能类似于 Junit 4 的 @Before 注解。):
/** * @author mghio * @date: 2020-05-30 * @version: 1.0 * @description: * @since JDK 1.8 */
@ExtendWith(MockitoExtension.class)
public class MockitoTest {
@Mock
private List<String> mockList;
@BeforeEach
public void beforeEach() {
MockitoAnnotations.initMocks(this);
}
}
验证性测试
Mockito 测试框架中提供了 Mockito.verify 静态方法让我们可以方便的进行验证性测试,比如方法调用验证、方法调用次数验证、方法调用顺序验证等,下面看看具体的代码。
验证方法单次调用
验证方法单次调用的话直接 verify 方法后加上待验证调用方法即可,以下代码的功能就是验证 mockList 对象的 size 方法被调用一次。
/** * @author mghio * @date: 2020-05-28 * @version: 1.0 * @description: * @since JDK 1.8 */
@ExtendWith(MockitoExtension.class)
public class MockitoVerifyTest {
@Mock
List<String> mockList;
@Test
void verify_SimpleInvocationOnMock() {
mockList.size();
verify(mockList).size();
}
}
验证方法调用指定次数
除了验证单次调用,我们有时候还需要验证一些方法被调用多次或者指定的次数,那么此时就可以使用 verify + times 方法来验证方法调用指定次数,同时还可以结合 atLeast + atMost 方法来提供调用次数范围,同时还有 never 等方法验证不被调用等。
/** * @author mghio * @date: 2020-05-28 * @version: 1.0 * @description: * @since JDK 1.8 */@ExtendWith(MockitoExtension.class)public class MockitoVerifyTest { @Mock List<String> mockList; @Test void verify_NumberOfInteractionsWithMock() { mockList.size(); mockList.size(); verify(mockList, times(2)).size(); verify(mockList, atLeast(1)).size(); verify(mockList, atMost(10)).size(); }}
验证方法调用顺序
同时还可以使用 inOrder 方法来验证方法的调用顺序,下面示例验证 mockList 对象的 size、add 和 clear 方法的调用顺序。
/** * @author mghio * @date: 2020-05-28 * @version: 1.0 * @description: * @since JDK 1.8 */
@ExtendWith(MockitoExtension.class)
public class MockitoVerifyTest {
@Mock
List<String> mockList;
@Test
void verify_OrderedInvocationsOnMock() {
mockList.size();
mockList.add("add a parameter");
mockList.clear();
InOrder inOrder = inOrder(mockList);
inOrder.verify(mockList).size();
inOrder.verify(mockList).add("add a parameter");
inOrder.verify(mockList).clear();
}
}
以上只是列举了一些简单的验证性测试,还有验证测试方法调用超时以及更多的验证测试可以通过相关官方文档探索学习。
验证方法异常
异常测试我们需要使用 Mockito 框架提供的一些调用行为定义,Mockito 提供了 when(...).thenXXX(...) 来让我们定义方法调用行为,以下代码定义了当调用 mockMap 的 get 方法无论传入任何参数都会抛出一个空指针 NullPointerException 异常,然后通过 Assertions.assertThrows 来验证调用结果。
/** * @author mghio * @date: 2020-05-30 * @version: 1.0 * @description: * @since JDK 1.8 */
@ExtendWith(MockitoExtension.class)
public class MockitoExceptionTest {
@Mock
public Map<String, Integer> mockMap;
@Test
public void whenConfigNonVoidReturnMethodToThrowEx_thenExIsThrown() {
when(mockMap.get(anyString())).thenThrow(NullPointerException.class);
assertThrows(NullPointerException.class, () -> mockMap.get("mghio"));
}
}
同时 when(...).thenXXX(...) 不仅可以定义方法调用抛出异常,还可以定义调用方法后的返回结果,比如 when(mockMap.get("mghio")).thenReturn(21); 定义了当我们调用 mockMap 的 get 方法并传入参数 mghio 时的返回结果是 21。这里有一点需要注意,使用以上这种方式定义的 mock 对象测试实际并不会影响到对象的内部状态,如下图所示:
虽然我们已经在 mockList 对象上调用了 add 方法,但是实际上 mockList 集合中并没有加入 mghio,这时候如果需要对 mock 对象有影响,那么需要使用 spy 方式来生成 mock 对象。
public class MockitoTest {
private List<String> mockList = spy(ArrayList.class);
@Test
public void add_spyMockList_thenAffect() {
mockList.add("mghio");
assertEquals(0, mockList.size());
}
}
断点后可以发现当使用 spy 方法创建出来的 mock 对象调用 add 方法后,mghio 被成功的加入到 mockList 集合当中。
- - - -未完待续- - - -
喜欢本文的朋友,欢迎关注公众号 并发编程网,收看更多精彩内容