
本文详解 Mockito 单元测试中 assertThrows 失败的常见原因,重点解决因手动构造被测对象导致依赖未正确注入、从而无法触发预期异常的问题,并提供基于 @InjectMocks 的标准实践与完整可运行示例。
本文详解 mockito 单元测试中 `assertthrows` 失败的常见原因,重点解决因手动构造被测对象导致依赖未正确注入、从而无法触发预期异常的问题,并提供基于 `@injectmocks` 的标准实践与完整可运行示例。
在使用 Mockito 编写异常处理逻辑的单元测试时,一个典型错误是:手动 new 出被测对象(SUT),却未将已 mock 的依赖注入其中。这会导致被测方法实际调用的是真实依赖(或 null 引用),而非我们预设抛出异常的 mock 对象,最终 assertThrows 因无异常抛出而失败。
你原始代码中的关键问题在于:
sut = new RewardsOperation(customerProfileService); // ✅ 传入了 mock,看似正确
但请注意:customerProfileService 虽为 @Mock 字段,其行为需通过 when(...).thenThrow(...) 显式定义——而你的 setUp() 中虽创建了 restConnectorException,却未在 when() 中真正关联到 getCustomerProfile() 方法的调用上。更严重的是,你后续测试中又重复构建了 restConsumerRequest,但 when() 的 stubbing 使用的是 setUp() 中初始化的 restConsumerRequest 实例,而 assertThrows 中调用 sut.handle(...) 时传入的是新构造的请求对象(未被 stubbed),导致 stubbing 不生效。
✅ 正确做法是:统一使用 @InjectMocks 让 Mockito 自动完成依赖注入,并确保 stubbing 与实际调用使用同一请求对象(或使用匹配器)。
以下是推荐的、符合 Mockito 最佳实践的修复版本:
@ExtendWith(MockitoExtension.class)
class RewardsOperationTest {
@Mock
private CustomerProfileService customerProfileService;
@InjectMocks
private RewardsOperation rewardsOperation; // ✅ Mockito 自动注入 mock 到构造函数/字段
@Test
void handle_withRestConnectorException_shouldThrowAndHandle() {
// Given: 构建测试请求(复用同一实例,或使用 any() 匹配器)
RestConsumerRequest<pointsfulfillrequest> request =
RestConsumerRequest.<pointsfulfillrequest>builder()
.request(PointsFulfillRequest.buildExample())
.build();
Notification notification = Notification.builder()
.code("E_CBTCUST_PROFILE_2001")
.build();
RestConnectorException exception = new RestConnectorException(notification, new Exception());
// Stub 方法:当任意 Request 调用 getCustomerProfile 时抛出异常(更健壮)
when(customerProfileService.getCustomerProfile(any())).thenThrow(exception);
// When & Then: 验证 handle() 方法内部会抛出该异常(注意:此处验证的是业务逻辑是否触发异常,而非 try-catch 吞掉后的行为)
Assertions.assertThrows(RestConnectorException.class, () -> rewardsOperation.handle(request));
}
}</pointsfulfillrequest></pointsfulfillrequest>
? 关键要点总结:
- 禁用手动 new + 手动传参:避免绕过依赖注入机制,改用 @InjectMocks。
- Stubbing 使用匹配器(如 any())更安全:避免因请求对象引用不一致导致 stubbing 失效;若必须用具体对象,请确保 when() 和 handle() 传入的是同一个实例。
- assertThrows 验证的是方法执行路径是否真正抛出异常:本例中,handle() 内部 try-catch 会捕获并处理 RestConnectorException,因此若目标是验证 异常被正确捕获并转化为响应,应改为断言返回值(如 response.isError() 或特定错误码);而当前测试意图是验证“当底层服务抛异常时,上层是否能接收到该异常”,则需确保 handle() 方法本身不吞掉它(即 catch 块未完全屏蔽异常传播,或测试聚焦于 catch 块逻辑——此时应验证 handle() 返回值,而非抛出异常)。
- 检查 RewardsOperation 的 handle() 方法签名:若其声明 throws RestConnectorException,则 assertThrows 合理;若该异常被 catch 后静默处理或转为其他响应,则 assertThrows 必然失败——此时测试重点应转向验证 RestConsumerResponse 的 error 状态。
遵循以上原则,即可精准定位并修复 Mockito 异常测试失败问题,提升测试可靠性与可维护性。











