Mockito Spy消息队列消费者的测试有效性及验证报错问题
问题解答
1. 针对消息队列使用Spy是否可行?
不可行,当前场景下Spy完全无法感知消息消费行为,原因如下:
- 你的
Subscriber类中,subscribeInput()是@Bean方法,Spring容器启动时会调用一次该方法,把返回的Consumer<InputVO>实例注册为容器Bean。后续消息消费时,是直接调用这个Consumer实例的accept()方法,不会再调用Subscriber的subscribeInput()方法。 - 你手动创建的
subscriberSpy只是原Subscriber实例的包装,但Spring容器并没有使用这个Spy实例,实际处理消息的还是容器中原有的Consumer实例,和Spy对象没有任何关联。 - 正如你查到的,Spy只能追踪通过Spy对象直接发起的方法调用,当前消费流程完全绕开了Spy,所以根本无法感知到消息消费行为。
2. 验证Spy时始终抛出异常的原因
抛出UnfinishedVerificationException的核心原因是:你要验证的subscribeInput()方法在测试流程中根本没被subscriberSpy调用过,结合Spring Bean和Mockito的特性,引发了异常提示:
- 测试启动时,Spring容器调用
subscribeInput()创建Consumer Bean,这次调用是针对原Subscriber实例,而非你创建的subscriberSpy。后续消息消费阶段,不会再触发subscribeInput()方法的调用,所以Spy的方法调用记录为空。 - 为什么不是常见的“Wanted but not invoked”异常?可能是因为
subscribeInput()是Spring的@Bean生命周期方法,Mockito对这类方法的代理存在兼容性问题,导致验证时无法正确识别方法调用状态,从而抛出“未完成模拟”的异常。 - 老师提到的混合使用
@MockBean和Spy不是直接原因,但@MockBean替换InputService确实让场景更复杂,核心问题还是Spy没有参与实际消费流程,且验证的方法从未被Spy调用。
解决方案建议
如果要验证消费逻辑,应该直接针对Consumer<InputVO>实例做Spy,而不是Subscriber类:
@SpringBootTest @Import(TestChannelBinderConfiguration.class) public class SubscriberTest { @Autowired StreamBridge streamBridge; @MockBean InputService inputService; // 使用@SpyBean让Spring自动创建并注入Spy的Consumer实例 @SpyBean(name = "subscribeInput") private Consumer<InputVO> subscribeInputConsumer; @Test public void subscriberMethod_WillBeCalled_WhenInputReceived() throws InterruptedException { InputVO expected = new InputVO(); streamBridge.send("binding", expected); TimeUnit.SECONDS.sleep(2); // 验证Consumer的accept方法被调用 verify(subscribeInputConsumer, times(1)).accept(expected); verify(inputService, times(1)).someMethod(expected); } }
内容的提问来源于stack exchange,提问作者teamol
相关产品推荐
相关产品推荐

