Quarkus中Mock MutinyEmitter遇CDI异常,如何完成单元测试?
解决Dependent作用域下MutinyEmitter的单元测试问题
针对你遇到的Dependent作用域Bean无法被Mockito的@InjectMocks正常注入的问题,下面提供几种实用的解决方法:
方法一:用Quarkus官方Mock扩展(推荐,适合Quarkus环境)
如果你的项目基于Quarkus,直接用官方提供的测试扩展就能避开作用域冲突问题,这是最省心的方案:
- 测试类添加
@QuarkusTest注解,开启Quarkus测试上下文 - 用
@InjectMock结合@Channel创建对应通道的Mock实例 - 注入待测试的
PaymentReversalService,调用方法后验证Mock的调用情况
代码示例:
import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.mockito.InjectMock; import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.MutinyEmitter; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import javax.inject.Inject; import org.eclipse.microprofile.reactive.messaging.Channel; @QuarkusTest public class PaymentReversalServiceTest { @InjectMock @Channel("payment-reversal-channel") // 替换为你实际的通道名称 MutinyEmitter<Payment> mockEmitter; @Inject PaymentReversalService service; @Test void testReverse() { Payment testPayment = new Payment(); // 预设Mock的send方法返回值 Mockito.when(mockEmitter.send(Mockito.any(Payment.class))).thenReturn(Uni.createFrom().item(testPayment)); // 调用待测试方法 Uni<Payment> result = service.reverse(testPayment); // 验证Mock调用和结果 result.subscribe().with(payment -> { Mockito.verify(mockEmitter, Mockito.times(1)).send(testPayment); }); } }
方法二:手动注入Mock(纯Mockito环境通用)
如果不想依赖Quarkus测试扩展,直接手动实例化服务并注入Mock,完全绕开CDI作用域限制:
- 用Mockito创建
MutinyEmitter的Mock实例 - 实例化
PaymentReversalService - 用反射工具(比如Mockito自带的
ReflectionTestUtils)把Mock注入到服务的对应字段 - 执行测试并验证
代码示例:
import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.MutinyEmitter; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.test.util.ReflectionTestUtils; public class PaymentReversalServiceTest { @Test void testReverse() { // 创建Mock Emitter MutinyEmitter<Payment> mockEmitter = Mockito.mock(MutinyEmitter.class); Payment testPayment = new Payment(); Mockito.when(mockEmitter.send(testPayment)).thenReturn(Uni.createFrom().item(testPayment)); // 实例化待测试服务并注入Mock PaymentReversalService service = new PaymentReversalService(); ReflectionTestUtils.setField(service, "emitter", mockEmitter); // "emitter"为服务中对应字段名 // 执行测试并验证 service.reverse(testPayment).await().indefinitely(); Mockito.verify(mockEmitter, Mockito.times(1)).send(testPayment); } }
如果你的PaymentReversalService支持构造器注入(把Emitter作为构造参数),直接通过构造器传入Mock实例即可,无需反射,代码会更简洁。
方法三:自定义CDI生产者(适合复杂CDI测试场景)
如果必须在CDI测试上下文运行,可以自定义生产者提供Mock实例,覆盖原通道的注入:
- 在测试类中创建CDI生产者方法,标注
@Produces和@Channel,返回Mock的MutinyEmitter - 给生产者设置
@ApplicationScoped作用域,避免Dependent作用域问题 - 注入服务进行测试
代码示例:
import io.smallrye.mutiny.Uni; import io.smallrye.reactive.messaging.MutinyEmitter; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Produces; import javax.inject.Inject; import org.eclipse.microprofile.reactive.messaging.Channel; public class PaymentReversalServiceTest { private final MutinyEmitter<Payment> mockEmitter = Mockito.mock(MutinyEmitter.class); @Produces @Channel("payment-reversal-channel") @ApplicationScoped public MutinyEmitter<Payment> produceMockEmitter() { return mockEmitter; } @Inject PaymentReversalService service; @Test void testReverse() { Payment testPayment = new Payment(); Mockito.when(mockEmitter.send(testPayment)).thenReturn(Uni.createFrom().item(testPayment)); service.reverse(testPayment).await().indefinitely(); Mockito.verify(mockEmitter).send(testPayment); } }
内容的提问来源于stack exchange,提问作者Allan Juan
相关产品推荐
相关产品推荐

