Mockito:如何模拟并断言抛出异常,解决不必要存根错误
问题分析
你遇到的unnecessary Mockito stubbings错误,本质是你给某个方法做了存根(stub),但整个测试过程中这个存根的方法根本没被调用。看你的测试代码,你存根了securityManagerService.restoreRight("a")会抛出异常,但测试方法里既没直接调用它,也没通过调用requireRight间接触发它,Mockito的默认严格校验就会报错。
另外,你的代码还有个冗余问题:@Mock注解和手动mock(SecurityManagerService.class)重复了——用了@RunWith(MockitoJUnitRunner.class)之后,@Mock会自动帮你创建Mock实例,不需要手动调用mock方法,这部分可以删掉。
正确实现步骤
要测试requireRight方法在restoreRight抛出异常时的行为,你需要:
- 移除冗余的Mock初始化代码
- 确保测试中实际调用
requireRight方法(触发内部的restoreRight调用,用到你存根的逻辑) - 用合适的方式断言异常的抛出
修正后的代码(JUnit 4)
@RunWith(MockitoJUnitRunner.class) public class SecurityManagerServiceTest { // 去掉手动mock,@Mock会自动初始化实例 @Mock private SecurityManagerService securityManagerService; @Test(expected = SecurityException.class) // JUnit4 基础异常断言方式 public void testRequireAll_WhenRestoreRightThrows_ShouldThrowSecurityException() { // 1. 存根loggerUser方法,返回模拟用户 when(securityManagerService.loggerUser()).thenReturn(fakeUser()); // 2. 指定调用requireRight时执行真实方法(测试真实业务逻辑) doCallRealMethod().when(securityManagerService).requireRight(anyString()); // 3. 存根restoreRight("a")抛出异常 when(securityManagerService.restoreRight("a")).thenThrow(SecurityException.class); // 4. 关键:调用requireRight,触发内部的restoreRight调用 securityManagerService.requireRight("a"); } // 假设你的模拟用户生成方法是这样的 private User fakeUser() { User user = new User(); // 按需设置用户属性 return user; } }
更灵活的异常断言(推荐)
如果用JUnit 4的expected注解,只能断言抛出指定异常,无法对异常做更多验证(比如异常消息)。你可以用try-catch结合断言,或者配合Mockito的验证方法:
@Test public void testRequireAll_WhenRestoreRightThrows_ShouldThrowSecurityException() { // 前置存根逻辑不变 when(securityManagerService.loggerUser()).thenReturn(fakeUser()); doCallRealMethod().when(securityManagerService).requireRight(anyString()); when(securityManagerService.restoreRight("a")).thenThrow(new SecurityException("权限验证失败")); // 捕获并验证异常细节 try { securityManagerService.requireRight("a"); // 如果没抛出异常,直接标记测试失败 fail("Expected SecurityException to be thrown"); } catch (SecurityException e) { assertEquals("权限验证失败", e.getMessage()); // 可选:验证restoreRight确实被调用了一次 verify(securityManagerService).restoreRight("a"); } }
关于Mockito严格模式
如果你确实有存根但暂时不想被校验(不推荐长期使用),可以在测试类上添加@MockitoSettings(strictness = Strictness.LENIENT)关闭严格模式,但最好的方式还是确保所有存根都被用到,或者直接移除不必要的存根。
内容的提问来源于stack exchange,提问作者Nunyet Calçada
相关产品推荐
相关产品推荐

