严格Stubbing(Strict Stubbing)是Mockito提供的一种增强测试严谨性的模式,旨在检测以下问题:
- 多余的Stubbing:配置了未被调用的方法桩。
- 不必要的Stubbing:Stubbing未被使用且不影响测试结果。
- 桩顺序错误:Stubbing被覆盖或错误使用。
通过严格模式,可以避免测试代码中的“虚假通过”(False Positive),确保每个Stubbing都有明确的目的。
1. 启用严格Stubbing
Mockito提供了两种方式启用严格模式:
1.1 全局启用(推荐)
在测试类上添加 @MockitoSettings(strictness = Strictness.STRICT_STUBS)
:
java">@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.STRICT_STUBS) // 全局启用严格模式
public class OrderServiceTest {// 测试代码...
}
1.2 手动启用
通过 MockitoSession
在单个测试中启用:
java">@Test
void testWithManualStrictness() {try (MockitoSession session = Mockito.mockitoSession().strictness(Strictness.STRICT_STUBS).startMocking()) {// 测试代码...}
}
2. 严格模式下的错误场景与示例
2.1 多余的Stubbing
问题:配置了未被调用的Stubbing。
示例:
java">@Mock
private UserDao mockUserDao;@Test
void findUser_ShouldReturnUser() {// 多余的Stubbing:findByEmail未被调用when(mockUserDao.findByUsername("alice")).thenReturn(new User());when(mockUserDao.findById(1)).thenReturn(new User()); // 实际被调用的StubbingUser user = userService.findUserById(1); // 调用findById(1)verify(mockUserDao).findById(1);
}
错误信息:
Unnecessary stubbings detected.
2.2 不必要的Stubbing
问题:Stubbing存在但未被使用,且不影响测试结果。
示例:
java">@Test
void createUser_ShouldNotDependOnFindMethod() {when(mockUserDao.findByUsername("alice")).thenReturn(null); // 未被使用doNothing().when(mockUserDao).save(any());userService.createUser("alice", "pass123");verify(mockUserDao).save(any());
}
错误信息:同上,提示存在不必要的Stubbing。
3. 解决方案
3.1 删除多余的Stubbing
直接移除未被调用的桩代码:
java">@Test
void findUser_ShouldReturnUser() {// 删除多余的findByUsername桩when(mockUserDao.findById(1)).thenReturn(new User());User user = userService.findUserById(1);verify(mockUserDao).findById(1);
}
3.2 显式标记宽松Stubbing
使用 lenient()
忽略特定Stubbing的检测:
java">@Test
void createUser_ShouldAllowUnusedStubbing() {// 标记为宽松Stubbinglenient().when(mockUserDao.findByUsername("alice")).thenReturn(null);doNothing().when(mockUserDao).save(any());userService.createUser("alice", "pass123");verify(mockUserDao).save(any());
}
4. 严格模式的进阶用法
4.1 验证Stubbing顺序
严格模式会检测Stubbing的覆盖顺序。例如,重复定义同一方法的Stubbing可能导致意外覆盖:
java">@Test
void testStubbingOrder() {when(mockList.get(0)).thenReturn("A");when(mockList.get(0)).thenReturn("B"); // 覆盖前一个StubbingassertEquals("B", mockList.get(0)); // 通过
}
严格模式下会提示 PotentialStubbingProblem
,建议使用 doReturn()
链式配置:
java">when(mockList.get(0)).thenReturn("A").thenReturn("B");
4.2 检测无效的Stubbing参数
严格模式会检查参数匹配器的使用是否正确:
java">@Test
void testInvalidMatcher() {// 错误:混合精确值和匹配器when(mockUserDao.find(eq("alice"), anyString())).thenReturn(null);
}
错误信息:
Invalid use of argument matchers!
5. 最佳实践
- 仅Mock必要的交互:避免过度Mock,仅覆盖影响当前测试的依赖方法。
- 及时清理无用桩代码:定期运行测试并修复严格模式提示的问题。
- 合理使用
lenient()
:仅在明确需要时放宽检测,如共享测试配置中的通用桩。 - 结合日志分析:通过
Mockito.mockingDetails(mock).printInvocations()
调试Stubbing调用。
总结
严格Stubbing模式通过强制检测冗余和无效的桩代码,显著提升单元测试的健壮性和可维护性。结合示例中的修复策略,开发者可以快速定位问题并优化测试逻辑,确保每个Stubbing都精准服务于测试目标。