
在 Quarkus 中使用 @InjectMock 时,被测类的所有方法默认被空实现覆盖,导致内部方法调用无法触发真实逻辑;需显式配置 thenCallRealMethod() 才能实现对类内方法(如 get())的可控模拟。
在 quarkus 中使用 `@injectmock` 时,被测类的所有方法默认被空实现覆盖,导致内部方法调用无法触发真实逻辑;需显式配置 `thencallrealmethod()` 才能实现对类内方法(如 `get()`)的可控模拟。
在 Quarkus 集成测试中,@InjectMock 注入的是一个 全量 Mockito Mock 实例,其行为与普通 mock() 一致:所有方法(包括 sendRequest 和 get)均被替换为 stub 方法——返回默认值(如 null、0 或空集合),且不会执行原始业务逻辑。因此,当你调用 service.sendRequest(1, "name") 时,该方法本身并未真正执行,自然也不会调用内部的 get() 方法,更无法触发你期望的异常路径。
要解决此问题,必须让 sendRequest 执行真实逻辑,同时仅对 get() 方法进行定制化模拟(如抛出异常)。关键在于:启用部分真实方法调用(partial mocking)。
✅ 正确做法是:
- 使用 @InjectMock 获取服务实例;
- 显式声明 sendRequest(...) 调用真实实现;
- 单独 stub get(...) 方法的行为(如抛异常)。
以下是修正后的完整测试代码:
@QuarkusTest
public class ServiceTest {
@InjectMock
Service service;
@Test
void testSendRequest_WhenGetThrowsRuntimeException_ThenMyExceptionIsThrown() {
// Step 1: 让 sendRequest 调用真实方法体(否则内部 get() 永远不会执行)
when(service.sendRequest(eq(1), eq("name"))).thenCallRealMethod();
// Step 2: 模拟 get() 方法抛出 RuntimeException
when(service.get(eq(1), eq("name"))).thenThrow(new RuntimeException("MyError"));
// Step 3: 执行并验证异常
MyException thrown = assertThrows(MyException.class, () -> service.sendRequest(1, "name"));
assertEquals("Error during get data MyError", thrown.getMessage());
}
}
⚠️ 注意事项:
- eq() 比 anyInt()/anyString() 更安全:避免因参数匹配过于宽泛导致意外 stub 生效;
- thenCallRealMethod() 必须在 when(...).thenCallRealMethod() 中明确指定具体方法签名,不可省略参数匹配;
- 若 sendRequest 有多个重载,需确保 when() 的参数类型与调用完全一致;
- 不推荐对 @ApplicationScoped Bean 直接使用 @InjectMock 进行部分 mock —— 更健壮的设计是将 get() 抽离为独立可注入组件(如 DataClient),再对其单独 mock,从而避免部分 mock 带来的可维护性风险。
总结:Quarkus 的 @InjectMock 并非“仅 mock 某些方法”,而是“默认 mock 全部方法”。若需测试方法内部协作逻辑,请主动启用 thenCallRealMethod(),并精准控制待模拟的依赖点。这是保障单元测试真实性和可读性的关键实践。











