Mockito 注解、插桩、验证、断言

2025-08-05  本文已影响0人  flyjar

Mockito 注解、插桩、验证、断言、mockspy 等关键内容:

一、Mockito 基础与核心注解

1. 核心依赖与配置

二、Mock 与 Spy 的区别

特性 Mock 对象 Spy 对象
本质 完全虚拟的对象(无真实实现) 基于真实对象的监视(可复用真实逻辑)
默认行为 未插桩方法返回空值(null0 等) 未插桩方法执行真实对象的逻辑
适用场景 完全隔离外部依赖(如数据库、网络) 复用大部分真实逻辑,仅修改部分行为
插桩推荐方式 when(mock.method()).thenReturn(...) doReturn(...).when(spy).method()
副作用 无(不调用真实方法) 有(可能修改真实对象状态)

示例

@ExtendWith(MockitoExtension.class)
class MockSpyTest {
    @Mock
    private List<String> mockList; // 完全模拟的List
    
    @Spy
    private List<String> spyList = new ArrayList<>(); // 基于真实ArrayList的监视对象

    @Test
    void testMock() {
        // Mock默认返回空值,需手动插桩
        when(mockList.size()).thenReturn(10);
        assertEquals(10, mockList.size());
    }

    @Test
    void testSpy() {
        // Spy默认执行真实方法
        spyList.add("item");
        assertEquals(1, spyList.size()); // 真实逻辑:size=1
        
        // 插桩覆盖部分行为(推荐用doReturn)
        doReturn(100).when(spyList).size();
        assertEquals(100, spyList.size()); // 插桩生效
    }
}

三、插桩(Stubbing):预设方法行为

插桩是为模拟对象预设返回值、异常或自定义行为,确保测试不受外部依赖影响。

1. 基础插桩方式

2. 特殊场景插桩

四、验证(Verify):检查方法调用行为

验证用于确认 mock/spy 对象的方法是否按预期被调用(次数、参数等)。

常用验证方法

// 验证方法被调用过1次(默认)
verify(mockList).add("item");

// 验证调用次数
verify(mockList, times(3)).add(anyString()); // 调用3次
verify(mockList, never()).remove(any()); // 从未调用

// 验证调用顺序
InOrder inOrder = inOrder(mockService, mockRepo);
inOrder.verify(mockService).update(any());
inOrder.verify(mockRepo).save(any()); // 确保update在save之前调用

// 验证无多余调用
verifyNoMoreInteractions(mockList);

五、断言(Assertion):验证结果正确性

结合 JUnit 5 的 Assertions 工具类,验证测试结果是否符合预期。

import static org.junit.jupiter.api.Assertions.*;

@Test
void testResult() {
    User user = userService.getUser(1L);
    
    assertNotNull(user); // 非空断言
    assertEquals("Alice", user.getName()); // 相等断言
    assertTrue(user.getAge() > 18); // 条件断言
    
    // 异常断言
    assertThrows(RuntimeException.class, () -> {
        userService.getUser(99L);
    });
}

六、综合示例

测试 OrderService 依赖 ProductDao 的场景:

public class OrderService {
    private final ProductDao productDao;
    
    public OrderService(ProductDao productDao) {
        this.productDao = productDao;
    }
    
    public double calculateTotal(List<Long> productIds) {
        double total = 0;
        for (Long id : productIds) {
            total += productDao.getPrice(id);
        }
        return total;
    }
}

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
    @Mock
    private ProductDao productDao;
    
    @InjectMocks
    private OrderService orderService;

    @Test
    void calculateTotal_ShouldReturnSum() {
        // 1. 插桩:预设商品价格
        when(productDao.getPrice(1L)).thenReturn(100.0);
        when(productDao.getPrice(2L)).thenReturn(200.0);
        
        // 2. 执行测试
        double total = orderService.calculateTotal(List.of(1L, 2L));
        
        // 3. 断言结果
        assertEquals(300.0, total, 0.01);
        
        // 4. 验证调用
        verify(productDao, times(2)).getPrice(anyLong()); // 调用2次
    }
}

总结

掌握这些核心概念,可以编写简洁、可靠的单元测试,有效隔离外部依赖并验证代码逻辑。

上一篇 下一篇

猜你喜欢

热点阅读