You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确Mock带有@Startup注解的CDI Bean?

解决Quarkus中Mock @Startup注解CDI Bean的问题

问题原因

标注@Startup的CDI Bean会在应用启动阶段被优先实例化,而普通的@InjectMock(Mockito注解)是在测试类初始化时才注入Mock对象,此时原Bean已经被创建并注入到目标资源类中,导致Mock替换失效,原Bean的构造方法仍会执行。

解决方案

方案1:使用Quarkus官方的@MockBean注解

Quarkus提供的@MockBean注解(来自quarkus-junit5-mockito扩展)会在应用启动前就替换掉CDI容器中的原Bean,确保原Bean不会被实例化,同时让目标资源类注入的是Mock对象。

步骤:

  1. 确保测试依赖中包含quarkus-junit5-mockito:
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-junit5-mockito</artifactId>
    <scope>test</scope>
</dependency>
  1. 修改测试类,将@InjectMock替换为@MockBean:
@QuarkusTest
class ProxyResourceTest {

    @MockBean
    private ApiManager apiManagerMock;

    @Test
    void testProxyEndpointReturns200AndResponse() {
        String payload = "expected response";

        Mockito.when(apiManagerMock.fetchResponse()).thenReturn(payload);

        given().contentType(ContentType.JSON)
                .body(payload)
                .when().post("/proxy")
                .then()
                .statusCode(200)
                .body(is(payload));
    }
}

执行测试时,原ApiManager的构造方法不会被调用,ProxyResource注入的是Mock对象,完全由Mockito控制行为。

方案2:拆分@Startup与业务Bean的职责

将原Bean的@Startup注解移除,改为@ApplicationScoped,并把启动初始化逻辑移到@PostConstruct方法中,再通过配置指定该Bean为启动Bean。

修改原ApiManager:

@ApplicationScoped
public class ApiManager {
    private Logger logger = Logger.getLogger(ApiManager.class);

    @PostConstruct
    void init() {
        logger.info("ApiManager warm-up tasks executed");
        // 执行预热操作,比如注册定时任务等
    }

    public String fetchResponse() {
        return "expected response";
    }
}

添加配置(application.properties):

# 指定该Bean在应用启动时初始化
quarkus.arc.startup-beans=com.yourpackage.ApiManager

此时原Bean的启动初始化逻辑依然保留,同时可以正常使用@InjectMock或@MockBean来Mock,因为它是标准的@ApplicationScoped Bean,Mock替换逻辑能正常生效。

方案3:使用@Alternative + @Priority替换原Bean

在测试类中定义一个带@Alternative和高优先级的Mock Bean,让CDI容器优先选择该Bean实例化,完全绕过原@Startup Bean。

测试类代码:

@QuarkusTest
class ProxyResourceTest {

    @Inject
    private ApiManager apiManager;

    // 定义替代Bean,优先级高于原Bean
    @Alternative
    @Priority(1)
    @Startup
    static class MockApiManager extends ApiManager {
        // 不调用父类构造方法,避免原初始化逻辑执行
        public MockApiManager() {}

        @Override
        public String fetchResponse() {
            return "mock response";
        }
    }

    @Test
    void testProxyEndpointReturns200AndResponse() {
        String payload = "expected response";
        // 用Mockito动态控制返回值
        ApiManager spyApiManager = Mockito.spy(apiManager);
        Mockito.when(spyApiManager.fetchResponse()).thenReturn(payload);

        given().contentType(ContentType.JSON)
                .body(payload)
                .when().post("/proxy")
                .then()
                .statusCode(200)
                .body(is(payload));
    }
}

这种方式下,原ApiManager的构造方法不会被执行,CDI容器会实例化MockApiManager并注入到ProxyResource中。

内容的提问来源于stack exchange,提问作者webcitron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 04:25:08