Skip to content

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

特性MockSpy
默认行为所有方法被 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参数捕获器捕获方法参数
@MockitoSettingsMockito 配置strictness 等
@MockBeanSpring Bean MockSpring Boot 测试

相关页面

  • **Java\JUnit5-完全指南** - 返回总览
  • **Java\JUnit5-断言机制** - 与 Mockito 配合使用
  • **Java\SpringBoot-测试专题** - Spring 环境下的 Mock
  • **Java\JUnit5-最佳实践** - 测试设计模式

#标签 #mockito #mock #spy #testing #unit-test #java

Knowledge4J — Java 知识库