QuarkusTest如何在JUnit5扩展中访问测试上下文Bean
你遇到的java.lang.IllegalStateException: Unable to locate CDIProvider异常,本质是原生JUnit 5扩展的回调执行时机早于Quarkus测试CDI容器的初始化节点,此时CDI上下文还没绑定到当前线程,直接调用CDI.current()自然无法找到容器实例。
Quarkus测试框架提供了和Spring SpringExtension.getApplicationContext()完全对等的能力,不需要在测试类中通过@Inject注入Bean,可以直接封装成可复用的通用扩展。
核心API说明
Quarkus的JUnit 5集成模块提供了io.quarkus.test.junit.QuarkusTestExtension工具类,你可以通过它的getBeanManager(ExtensionContext context)静态方法直接获取当前测试上下文关联的CDI BeanManager,拿到BeanManager后就可以正常查询、获取任意CDI Bean实例。
方法级回调实现(BeforeEach/AfterEach)
如果只需要在每个测试方法执行前后操作Bean,直接实现JUnit原生的BeforeEachCallback/AfterEachCallback接口即可,在回调方法中先通过上述API获取BeanManager,再操作CDI容器:
import io.quarkus.test.junit.QuarkusTestExtension; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.CDI; public class ReusableTestExtension implements BeforeEachCallback, AfterEachCallback { @Override public void beforeEach(ExtensionContext extensionContext) { // 触发BeanManager加载,完成CDI上下文绑定 BeanManager beanManager = QuarkusTestExtension.getBeanManager(extensionContext); // 直接获取目标Bean实例,和Spring context.getBean()逻辑一致 MyBean targetBean = CDI.current().select(MyBean.class).get(); // 编写自定义Bean交互逻辑 } @Override public void afterEach(ExtensionContext extensionContext) { BeanManager beanManager = QuarkusTestExtension.getBeanManager(extensionContext); MyBean targetBean = CDI.current().select(MyBean.class).get(); // 编写测试执行后的收尾逻辑 } }
类级回调实现(BeforeAll/AfterAll)
如果需要在测试类所有方法执行前的类级别节点操作Bean,不要直接使用JUnit原生的BeforeAllCallback/AfterAllCallback接口,这类回调的执行时机不受Quarkus控制,会出现容器未初始化的问题。你需要实现Quarkus自定义的对应回调接口,框架会保证回调触发时CDI容器已经完成启动:
import io.quarkus.test.junit.QuarkusTestExtension; import org.junit.jupiter.api.extension.ExtensionContext; import javax.enterprise.inject.spi.CDI; public class ReusableTestExtension implements QuarkusTestExtension.BeforeAllCallback, QuarkusTestExtension.AfterAllCallback { @Override public void beforeAll(ExtensionContext context) { // 此处CDI容器已完成初始化,可直接获取Bean MyBean targetBean = CDI.current().select(MyBean.class).get(); // 编写类级别的初始化逻辑 } @Override public void afterAll(ExtensionContext context) { MyBean targetBean = CDI.current().select(MyBean.class).get(); // 编写类级别的销毁逻辑 } }
注意事项
- 上述扩展仅支持标注了
@QuarkusTest的测试类使用,和Spring测试扩展仅能在Spring测试上下文使用的限制一致 - 不要在扩展的构造方法、原生JUnit的非测试方法级回调中直接调用
CDI.current(),这类节点执行时Quarkus容器可能未完成初始化 - 如果需要对Bean做条件匹配(比如指定名称、指定限定符),既可以用
CDI.current().select()的重载方法传入匹配规则,也可以直接通过拿到的BeanManagerAPI做更灵活的实例查询
内容的提问来源于stack exchange,提问作者Philipp Li

