如何在Quarkus中结合Testcontainers使用并实现依赖注入?
在Quarkus中结合Testcontainers与依赖注入的正确姿势
你遇到的问题核心原因很明确:单纯使用@Testcontainers注解并不会启动Quarkus的依赖注入(DI)容器,只有@QuarkusTest注解会触发Quarkus测试上下文的初始化,包括DI容器、配置加载等核心功能。只要保留@QuarkusTest,再配合Testcontainers的配置,就能解决注入为空的问题。
下面是两种可行的实现方式:
方式一:同时使用@QuarkusTest和@Testcontainers
直接在测试类上同时添加两个注解,通过系统属性覆盖Quarkus的数据库配置,让应用连接到Testcontainers启动的MariaDB实例:
import io.quarkus.test.junit.QuarkusTest; import org.testcontainers.containers.MariaDBContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; @QuarkusTest @Testcontainers public class TeamServiceTest { // static修饰保证容器在测试类生命周期内只启动一次 @Container private static final MariaDBContainer<?> mariaDBContainer = new MariaDBContainer<>("mariadb:10.6") .withDatabaseName("testdb") .withUsername("testuser") .withPassword("testpass"); static { // 在Quarkus启动前覆盖数据库配置,确保应用连接到Testcontainers容器 System.setProperty("quarkus.datasource.jdbc.url", mariaDBContainer.getJdbcUrl()); System.setProperty("quarkus.datasource.username", mariaDBContainer.getUsername()); System.setProperty("quarkus.datasource.password", mariaDBContainer.getPassword()); } @Inject TeamService teamService; @Test void verifyTeamServiceInjection() { assertNotNull(teamService, "TeamService 注入失败"); // 这里添加你的业务测试逻辑 } }
方式二:使用QuarkusTestResource(更优雅的配置方式)
如果想把容器管理和配置逻辑从测试类中抽离,可以自定义QuarkusTestResource实现类,这种方式更符合单一职责原则:
第一步:编写自定义TestResource类
import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; import org.testcontainers.containers.MariaDBContainer; import java.util.Map; public class MariaDBTestResource implements QuarkusTestResourceLifecycleManager { private MariaDBContainer<?> mariaDBContainer; @Override public Map<String, String> start() { // 启动MariaDB容器 mariaDBContainer = new MariaDBContainer<>("mariadb:10.6") .withDatabaseName("testdb") .withUsername("testuser") .withPassword("testpass"); mariaDBContainer.start(); // 返回需要覆盖的Quarkus配置 return Map.of( "quarkus.datasource.jdbc.url", mariaDBContainer.getJdbcUrl(), "quarkus.datasource.username", mariaDBContainer.getUsername(), "quarkus.datasource.password", mariaDBContainer.getPassword() ); } @Override public void stop() { // 测试结束后停止容器 if (mariaDBContainer != null) { mariaDBContainer.stop(); } } }
第二步:在测试类中使用该TestResource
import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.common.QuarkusTestResource; import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; @QuarkusTest @QuarkusTestResource(MariaDBTestResource.class) public class TeamServiceTest { @Inject TeamService teamService; @Test void verifyTeamServiceInjection() { assertNotNull(teamService); // 添加业务测试逻辑 } }
关键注意事项
- 必须保留@QuarkusTest:这个注解是Quarkus测试上下文的入口,没有它,DI容器不会初始化,@Inject的对象必然为空。
- 容器生命周期管理:使用static修饰容器实例(方式一)或通过TestResource(方式二),避免每个测试方法都重新启动容器,提升测试效率。
- 配置时机:确保数据库配置在Quarkus启动前被覆盖,这样应用才能正确连接到Testcontainers启动的数据库实例。
内容的提问来源于stack exchange,提问作者Tristate
相关产品推荐
相关产品推荐

