背景 #
网上Mockito 资料我看了一下很多都不够清晰,我总结一下我在使用 Mockito 常用的方法
Mockito 局限 #
- 无法模拟 final 方法(需 Mockito 4.10+ 支持 src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker 添加
mock-maker-inline
) - 静态方法不支持(需 Mockito 3.x+ 支持
MockedStatic<Utils> mockedStatic = mockStatic(Utils.class)
) - 私有方法,不支持(需要PowerMockito)也不推荐测试私有方法
Mockito 处理依赖注入 #
- 通过
@InjectMocks
注入到被测类(被测试类可以添加 Spy 注解 不可添加Mock注解) - 只会注入一层,嵌套的不会注册(且假如Mock对象上面有 InjectMocks 注解也不会将其注入) 代码必须不违背迪米特原则,就可以使用mock自动依赖注入
Mock 与 Spy 的区别总结 #
特性 | Mock 对象 | Spy 对象 |
---|---|---|
默认行为 | 所有方法默认被模拟,不执行真实逻辑 | 默认调用真实方法,需显式打桩才会模拟 |
创建方式 | mock(Class) 或 @Mock |
spy(真实对象) 或 @Spy |
适用场景 | 完全隔离依赖,避免外部交互(如数据库、API) | 部分保留真实逻辑,仅模拟特定方法(如耗时操作) |
对未打桩方法的处理 | 返回默认值(如 null 、0 ) |
执行真实方法 |
性能开销 | 轻量级,无真实对象实例化 | 需要创建真实对象,可能有初始化成本,构造器假如没有无参构造器会传入null |
在SpringBoot中使用 | @MockBean | @SpyBean |
Argument Matcher 常用总结 #
场景 | 推荐匹配器 | 示例 |
---|---|---|
精确值验证 | eq() |
eq("expected") |
类型无关匹配 | any() / any(Class<T>) |
any(User.class) |
基础类型通配 | anyInt() , anyString() |
when(...).thenReturn(...) |
复杂对象属性验证 | 自定义 ArgumentMatcher |
argThat(user -> ...) |
空值处理 | isNull() / nullable() |
nullable(String.class) |
Argument Matcher 用法对比表 #
场景 | 核心用法 | 示例 | 注意事项 | 引用来源 |
---|---|---|---|---|
verify() |
验证方法调用时的参数匹配规则,需全参数使用匹配器。 | verify(mock).someMethod(anyInt(), eq("test")) |
若方法有多个参数,只要一个参数使用匹配器,其他参数也需匹配器(如 eq() )否则抛异常。 |
1 5 8 |
when().thenReturn() |
对非 void 方法进行桩方法配置,参数匹配后返回指定值。 | when(mock.get(anyInt())).thenReturn("element") |
若方法为 void,需改用 doReturn() ;若使用 Spy 对象,真实方法会被执行,可能触发异常。 |
1 5 7 |
doReturn().when() |
对任意方法(包括 void)进行桩方法配置,直接覆盖行为,不执行真实逻辑。 | doReturn("mockValue").when(mock).method(argThat(s -> s.length() > 5)) |
适用于需要跳过真实方法执行的场景(如 Spy 对象、void 方法或异常方法)。 | 7 8 11 |
verify
方法用法对比表
#
参数形式 | 作用 | 常用模式(VerificationMode) | 示例 | 注意事项 |
---|---|---|---|---|
verify(mock) |
验证方法 是否被调用一次(默认次数为1)。 | 无(隐式调用 times(1) ) |
java<br>verify(mockList).add("test");<br> |
等同于 verify(mock, times(1)) 。若方法未被调用或次数不符,测试失败。 |
verify(mock, mode) |
验证方法 调用次数或特定行为,支持灵活的条件约束。 | - times(n) :精确调用次数 - atLeast(n) :至少调用次数 - atMost(n) :最多调用次数 - never() :从未调用 - atLeastOnce() :至少一次 -timeout(1000) : 异步超时 |
java<br>// 验证 add("test") 调用2次<br>verify(mockList, times(2)).add("test");<br><br>// 验证从未调用 clear()<br>verify(mockList, never()).clear(); |
1. 若混合使用参数匹配器(如 any() ),需确保所有参数均使用匹配器(如 eq() )。 2. 可组合使用 timeout() 验证异步调用超时。 |
JUnit Jupiter API 常用 Assertions 方法总结表:
断言方法 | 功能描述 | 示例 | 注意事项 | 引用来源 |
---|---|---|---|---|
assertEquals |
验证期望值(expected)与实际值(actual)是否相等。 | assertEquals(2, Math.addExact(1, 1)) |
对象比较使用 equals() 方法;支持通过 Supplier 延迟生成错误消息(失败时触发)1 6 。 |
1 6 8 |
assertTrue /assertFalse |
验证条件是否为真(或假)。 | assertTrue(1 < 2, "条件应为真") assertFalse(1 > 2) |
支持布尔表达式或 BooleanSupplier ,失败时显示自定义消息6 8 10 。 |
6 8 10 |
assertNull /assertNotNull |
验证对象是否为 null (或不为 null )。 |
assertNull(optional.orElse(null)) assertNotNull(new Object()) |
常用于验证方法返回值的空性 6 8 10 。 |
6 8 10 |
assertSame /assertNotSame |
验证两个对象引用是否指向同一对象(或不同对象)。 | assertSame(obj1, obj1) assertNotSame(new Object(), new Object()) |
使用 - 比较引用地址 6 8 10 。 |
6 8 10 |
assertArrayEquals |
验证两个数组的内容是否相等(包括长度和元素顺序)。 | assertArrayEquals(new int[]{1,2}, new int[]{1,2}) |
支持基本类型数组和对象数组;元素类型需一致 1 8 10 。 |
1 8 10 |
assertThrows |
验证执行代码块是否抛出指定类型的异常。 | assertThrows(ArithmeticException.class, () -> Math.floorDiv(1, 0)) |
返回异常实例,可进一步验证异常信息(如 getMessage() )1 6 8 。 |
1 6 8 |
assertTimeout |
验证代码块是否在指定时间内完成执行。 | assertTimeout(Duration.ofSeconds(1), () -> Thread.sleep(500)) |
超时后立即抛出失败;支持 Duration 时间单位1 8 9 。 |
1 8 9 |
assertAll |
批量验证多个断言,全部通过才算成功。 | assertAll("批量验证", () -> assertEquals(1,1), () -> assertTrue(2 > 1)) |
所有断言均会执行,失败时汇总所有错误信息 1 8 10 。 |
1 8 10 |
fail |
强制测试失败,用于标记未实现的测试或逻辑分支。 | fail("测试未完成") |
常用于异常分支或条件不满足时的快速终止 6 8 10 。 |
6 8 10 |
补充 #
Java8 Maven 依赖:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.11.0</version> <!-- Java 8 兼容版本 -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.11.0</version> <!-- 与 mockito-core 版本一致 -->
<scope>test</scope>
</dependency>