Mockito 是什么
Mockito 是一个用于单元测试的模拟框架,基于它可以使用简洁易用的API编写出色的测试。
Mockito 允许开发人员创建和管理模拟对象(mock objects),以便在测试过程中替换那些不容易构造或获取的对象。
Mockito的基本概念
- Mock对象:在调试期间用来作为真实对象的替代品。通过模拟对象,可以模拟外部依赖、交互行为等,从而使测试更加独立和可控。
- Mock测试:在测试过程中,对那些不容易构建的对象用一个虚拟对象来代替测试的方法就叫mock测试。
- Stub:存根,即为mock对象的方法指定返回值(可抛出异常)。
- Verify:行为验证,验证指定方法调用情况(是否被调用,调用次数等)。
Mockito的主要功能
- 模拟方法行为:Mockito允许对模拟对象的方法进行stubbing,即定义当调用某个方法时应该返回的值或抛出的异常。
- 验证交互行为:Mockito提供了丰富的API来验证模拟对象的交互行为,例如方法是否被调用、调用次数、参数匹配等。
- 参数匹配器:Mockito提供了参数匹配器,允许在验证方法调用时使用通配符或自定义匹配规则。
- 部分模拟:使用@Spy注解可以创建一个部分模拟对象,允许选择性地模拟对象中的某些方法。
选择Mockito作为模拟框架的原因
- StackOverflow庞大的社区将Mockito评为Java的最佳模拟框架
- 在2013年末对30,000个GitHub项目进行分析时,Mockito在所有库(不仅仅是测试工具)中位列前10名Java库。尽管Mockito在主报告中排名第九,但mockito-core和mockito-all是同一工具,因此Mockito的实际排名是第四,超过了诸如Guava或Spring等著名工具。将此研究视为Mockito每天对用Java编写的单元测试产生巨大影响的指标。
- 行为驱动开发(BDD)的创始人Dan North在2008年写道:"我们在主要会议期间决定使用JUnit 4和Mockito,因为我们认为它们是Java中TDD和模拟的未来。"
基于Maven 如何使用Mockito:
Mockito的使用步骤
- 添加依赖:在项目的构建文件(如Maven的pom.xml或Gradle的build.gradle)中添加Mockito的依赖。
- 设置测试类:使用@RunWith(MockitoJUnitRunner.class)注解测试类,或使用MockitoAnnotations.initMocks(this)在@Before方法中初始化模拟对象。
- 创建模拟对象:使用@Mock注解创建模拟对象,或使用Mockito.mock(Class)方法。
- 设置模拟行为:使用when(...).thenReturn(...)或doReturn(...).when(...)等方法设置模拟对象的行为。
- 编写测试方法:在测试方法中调用被测对象的方法,并使用verify(...)等方法验证交互行为。
- 运行测试:使用JUnit运行测试,并查看测试结果。
Mockito当前的最新版本是 5.14.2,在pom.xml中导入之后就可以开始使用了。
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.14.2</version>
</dependency>
使用场景1: 验证交互行为, 模拟方法是否被调用了?
创建Mock对象, 验证对象的方法是否被调用了。
/**
* Copyright (C) Oscar Chen(XM):
*
* Date: 2024-12-01
* Author: XM
*/
package com.osxm.test.mock;
import static org.mockito.Mockito.*; // 导入Mockito的静态方法
import java.util.List;
import org.junit.jupiter.api.Test;
public class MockitoTest {
@Test
public void mockitoDemo() {
// 创建mock对象
List mockedList = mock(List.class); // 创建一个List接口的mock对象
// 或者在Mockito 4.10.0及以上版本中,使用更简洁的方式
// List mockedList = mock();
// 使用mock对象,它不会抛出任何"意外交互"异常
mockedList.add("one");
mockedList.clear();
// 选择性、明确且高度可读的验证
verify(mockedList).add("one"); // 验证mock对象调用了add方法并传入了"one"
verify(mockedList).clear(); // 验证mock对象调用了clear方法
}
}
使用场景2:模拟方法调用
模拟方法执行返回的结果。
@Test
public void mockMethodCall(){
// 不仅可以模拟接口,还可以模拟具体类
LinkedList mockedList = mock(LinkedList.class); // 创建一个LinkedList类的mock对象
// 或者在Mockito 4.10.0及以上版本中,使用更简洁的方式
// LinkedList mockedList = mock();
// 在实际执行之前进行模拟(stubbing)
when(mockedList.get(0)).thenReturn("first"); // 当调用mockedList的get(0)方法时,返回"first"
// 以下打印输出为"first"
System.out.println(mockedList.get(0));
// 以下打印输出为"null",因为get(999)方法没有被模拟
System.out.println(mockedList.get(999));
}
主要参考
mock()
方法,或者@Mock
注解: 创建模拟对象when()/given()
指定模拟对象的行为spy()/@Spy
部门模拟, 真实方法会呼叫并且可以被验证和存根@InjectMocks
:自动注入用@Spy或@Mock注解的模拟/间谍字段verify()
: 检查方法是否使用给定参数被调用- 可以使用灵活的参数匹配,例如通过any()匹配任意表达式
- 或者使用@Captor捕获被调用的参数
- 可以使用BDDMockito进行行为驱动开发语法
Mockito的常用注解
- @RunWith:用于指定JUnit测试的运行器。对于Mockito,通常使用@RunWith(MockitoJUnitRunner.class)来运行测试。
- @Mock:用于创建模拟对象。
- @InjectMocks:用于将模拟对象注入到被测对象中。
- @Spy:用于创建部分模拟对象。
使用注意
不要干什么:
- 不要模拟不属于你的类型
- 不要模拟值对象
- 不要模拟一切
- 对测试表现一些爱心
不能干什么:
- 不能Mock静态方法:Mockito不支持对静态方法进行mock。
- 不能Mock private方法:Mockito不支持对private方法进行mock。
- 不能Mock final class:Mockito不支持对final类进行mock。
Mockito的集成与扩展
- 与JUnit集成:Mockito可以与JUnit无缝集成,使用@RunWith(MockitoJUnitRunner.class)即可。
- 与Spring集成:在Spring Boot项目中,可以使用@MockBean注解来模拟Spring上下文中的对象。
- 扩展功能:Mockito提供了丰富的扩展功能,如Mockito-inline用于支持内联Mocks的创建和使用等。