Mockito验证ApplicationEventPublisher方法从未调用失效问题
我有一个Spring Boot应用,编写了JUnit 5测试,希望确保applicationEventPublisher.publishEvent(any())从未被调用。但实际该方法被调用时测试仍通过;更奇怪的是,将times(0)(或never())改为times(2)时,测试会因方法仅被调用一次而失败。
测试代码如下:
@ExtendWith(MockitoExtension.class) public class EventHandlerTest { @Mock private ApplicationEventPublisher applicationEventPublisher; private EventHandler eventHandler; @BeforeEach void setUp() { releaseEventHandler = new ReleaseEventHandler(applicationEventPublisher); } @Test void doNothingWhenKafkaIsDisabled() { var event = new EventDTO(); eventHandler.accept(releaseEvent); verify(applicationEventPublisher, never()).publishEvent(any()); } }
我的代码中,EventHandler从Kafka主题读取事件,并生成新事件发布到Spring事件总线。我要验证的是在特定场景下内部事件不会被生成。所Mock的applicationEventPublisher是org.springframework.context.ApplicationEventPublisher接口。
更新:
经过测试,发现该问题似乎与ApplicationEventPublisher是函数式接口有关。该接口定义如下:
@FunctionalInterface public interface ApplicationEventPublisher { default void publishEvent(ApplicationEvent event) { publishEvent((Object) event); } void publishEvent(Object event); }
1. 修正测试代码的变量名错误(最可能的原因)
你的测试代码存在变量名不一致问题:setUp方法初始化的是releaseEventHandler,但测试方法中调用的是未初始化的eventHandler。这意味着测试根本没有执行到实际的业务逻辑,publishEvent自然不会被调用,所以never()验证会通过。
修复代码:
@BeforeEach void setUp() { // 将releaseEventHandler改为eventHandler,确保初始化的是测试用的实例 eventHandler = new ReleaseEventHandler(applicationEventPublisher); }
2. 明确指定方法的参数匹配类型
由于ApplicationEventPublisher是函数式接口,存在两个重载的publishEvent方法(默认方法接收ApplicationEvent,抽象方法接收Object),直接使用any()可能导致Mockito匹配到错误的方法签名,从而验证失效。
修复方案:
根据代码实际调用的方法,明确指定参数类型:
- 如果代码中调用的是抽象方法(或默认方法转发的抽象方法),验证时使用
any(Object.class):
verify(applicationEventPublisher, never()).publishEvent(any(Object.class));
- 如果需要验证默认方法的调用(特殊场景),可以指定
any(ApplicationEvent.class):
verify(applicationEventPublisher, never()).publishEvent(any(ApplicationEvent.class));
3. 升级Mockito版本
部分旧版本Mockito对函数式接口的默认方法支持存在bug,导致方法调用记录不准确。建议升级到最新稳定版本(如4.x或5.x系列),解决兼容性问题。
4. 验证测试逻辑的正确性
确保verify语句在业务方法调用之后执行,同时可以在对比场景(比如Kafka开启的情况)中验证publishEvent确实会被调用,排除测试场景本身的问题。
内容的提问来源于stack exchange,提问作者beni0888

