Mockito 完全指南
Java 领域最流行的 Mock 框架,用于隔离测试依赖
概述
Mockito 用于隔离测试依赖、验证行为、控制返回值。
Maven 依赖
xml
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
<!-- JUnit 5 集成 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>创建 Mock 对象
@Mock 注解 + @ExtendWith
java
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService;
}手动创建
java
@Test
void manualMock() {
List<String> mockList = mock(List.class);
when(mockList.size()).thenReturn(5);
assertEquals(5, mockList.size());
}设置 Mock 行为
when().thenReturn()
java
// 设置返回值
when(userRepository.findById(1L))
.thenReturn(Optional.of(new User("张三")));
// 连续调用返回不同值
when(userRepository.findById(1L))
.thenReturn(Optional.of(new User("张三")))
.thenReturn(Optional.of(new User("李四")))
.thenReturn(Optional.empty());
// 抛出异常
when(userRepository.findById(-1L))
.thenThrow(new IllegalArgumentException("ID 不能为负数"));doReturn().when()
java
// void 方法
doNothing().when(emailService).sendWelcomeEmail(anyString());
// 抛出异常
doThrow(new EmailException("发送失败"))
.when(emailService).sendWelcomeEmail("invalid-email");
// 连续调用不同行为
doReturn(true).doReturn(false).when(validator).isValid(param);Answer 动态返回值
java
when(userRepository.findById(anyLong()))
.thenAnswer(invocation -> {
Long id = invocation.getArgument(0);
return Optional.of(new User("User-" + id));
});验证调用
verify() 基本用法
java
@Test
void testBasicVerification() {
List<String> list = mock(List.class);
list.add("hello");
list.add("world");
verify(list, times(2)).add(anyString());
verify(list, atLeastOnce()).add("hello");
verify(list, atMost(3)).add(anyString());
verify(list, never()).clear();
}verify() 精确调用顺序
java
@Test
void testInteractionOrder() {
List<String> list = mock(List.class);
list.add("first");
list.add("second");
list.add("third");
InOrder inOrder = inOrder(list);
inOrder.verify(list).add("first");
inOrder.verify(list).add("second");
inOrder.verify(list).add("third");
}verify() 验证调用参数
java
@Test
void testArgumentVerification() {
List<String> list = mock(List.class);
list.add("hello");
verify(list).add("hello"); // 精确匹配
verify(list).add(anyString()); // 任意字符串
verify(list).add(startsWith("hel")); // 匹配器
verify(list).add(contains("llo")); // 包含
}ArgumentMatchers(参数匹配器)
常用匹配器
java
// 基本类型
anyInt(), anyLong(), anyString(), anyBoolean()
anyDouble(), anyFloat(), anyByte(), anyShort()
// 集合类型
anyList(), anySet(), anyMap(), anyCollection()
// 对象类型
any(User.class), any()
// 可空类型
nullable(String.class)
// 特定值
eq("hello"), same(obj)
// 自定义匹配
argThat(user -> user.getAge() >= 18)Spy
Spy 用于部分 Mock:真实调用部分方法,同时 Mock 其他方法。
java
@ExtendWith(MockitoExtension.class)
class SpyTest {
@Spy
private List<String> list = new ArrayList<>();
@Test
void testSpy() {
list.add("first");
list.add("second");
assertEquals(2, list.size()); // 真实调用
when(list.size()).thenReturn(100); // Mock
assertEquals(100, list.size());
}
}Spy vs Mock
| 特性 | Mock | Spy |
|---|---|---|
| 默认行为 | 所有方法被 Mock | 默认调用真实方法 |
| 状态 | 无状态 | 有真实状态 |
| 使用场景 | 隔离依赖 | 部分替代、监控 |
Spring Boot 集成
@MockBean
java
@SpringBootTest
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testGetUser() throws Exception {
when(userService.getById(1L))
.thenReturn(Optional.of(new User("张三", 25)));
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("张三"));
verify(userService).getById(1L);
}
}@WebMvcTest(轻量级)
java
@WebMvcTest(UserController.class)
@MockBean(JwtService.class)
@MockBean(UserService.class)
class UserControllerTest { }完整示例
java
@ExtendWith(MockitoExtension.class)
@DisplayName("订单服务测试")
class OrderServiceTest {
@Mock
private OrderRepository orderRepository;
@Mock
private PaymentGateway paymentGateway;
@InjectMocks
private OrderService orderService;
@Test
@DisplayName("支付成功时应创建订单并发送通知")
void createOrderSuccess() {
// Arrange
when(orderRepository.save(any(Order.class)))
.thenAnswer(inv -> inv.getArgument(0));
when(paymentGateway.process(any())).thenReturn(true);
// Act
Order order = orderService.create(new Order("商品A", 100.0));
// Assert
assertNotNull(order.getId());
assertEquals("PENDING", order.getStatus());
// Verify
verify(orderRepository).save(any());
verify(paymentGateway).process(any());
}
}常见问题
1. Mockito 无法 Mock final 类/方法
xml
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>5.11.0</version>
</dependency>2. "Unnecessary stubbings" 警告
java
@MockitoSettings(strictness = Strictness.LENIENT) // 宽松模式
// 或
@Mock(lenient = true) // 单个 Mock注解速查表
| 注解 | 作用 | 备注 |
|---|---|---|
@Mock | 创建 Mock 对象 | 需要 @ExtendWith |
@Spy | 创建 Spy 对象 | 部分 Mock |
@InjectMocks | 自动注入 Mock | 按类型注入 |
@Captor | 参数捕获器 | 捕获方法参数 |
@MockitoSettings | Mockito 配置 | strictness 等 |
@MockBean | Spring Bean Mock | Spring Boot 测试 |
相关页面
- **Java\JUnit5-完全指南** - 返回总览
- **Java\JUnit5-断言机制** - 与 Mockito 配合使用
- **Java\SpringBoot-测试专题** - Spring 环境下的 Mock
- **Java\JUnit5-最佳实践** - 测试设计模式
#标签 #mockito #mock #spy #testing #unit-test #java