You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中JUnit5测试:如何Mock方法内新建的EventA实例

如何Mock方法内部新建的EventA实例

你当前的测试代码无效,核心原因是测试中创建的EventA spy对象,和consumeEvents方法内部new EventA()生成的实例完全是两个独立对象,mock的extractModel()不会对方法里的真实EventA生效。再加上你未mockevent.getPayload()的返回值,导致eventA.setPayload(null),执行extractModel()时触发空指针异常。

以下是两种可行的解决方案:

方案一:重构代码(推荐,符合可测试性设计)

把EventA的创建逻辑抽成Consumer类的可覆盖方法,测试时通过mock这个方法返回我们可控的EventA实例:

修改Consumer类:

public class Consumer {
    
    private final ConsumerInterface consumerInterface;

    public Consumer(ConsumerInterface consumerInterface) {
        this.consumerInterface = consumerInterface;
    }

    public void consumeEvents(Event event) {
            EventA eventA = createEventA();
            eventA.setPayload(event.getPayload());
            consumerInterface.handleEvent(eventA.extractModel());
        } 
    }

    // 抽离创建逻辑,供测试覆盖
    protected EventA createEventA() {
        return new EventA();
    }
}

修改测试代码:

@ExtendWith(MockitoExtension.class)
class ConsumerTest {

    @Mock
    ConsumerInterface consumerInterface;
    @Mock
    Event event;
    @Mock
    EventPayload eventPayload;
    @Spy
    private Consumer consumer;

    @BeforeEach
    public void setup(){
        consumer = spy(new Consumer(consumerInterface));
    }

    @Test
    public void consumeEvents() {
        // mock event.getPayload()避免空指针
        when(event.getPayload()).thenReturn(new Payload()); // 替换为实际Payload类型或mock对象

        // 让Consumer的createEventA方法返回我们的spy EventA
        EventA eventASpy = spy(new EventA());
        doReturn(eventASpy).when(consumer).createEventA();
        // mock extractModel返回预设的eventPayload
        doReturn(eventPayload).when(eventASpy).extractModel();

        consumer.consumeEvents(event);

        verify(consumerInterface, times(1)).handleEvent(eventPayload);
    }
}

方案二:使用Mockito-inline mock构造函数(无需修改原代码)

如果不想改动原业务代码,可以使用Mockito的mockConstruction功能(需依赖mockito-inline),直接拦截EventA的构造调用并返回mock对象:

测试代码:

@ExtendWith(MockitoExtension.class)
class ConsumerTest {

    @Mock
    ConsumerInterface consumerInterface;
    @Mock
    Event event;
    @Mock
    EventPayload eventPayload;

    private Consumer consumer;

    @BeforeEach
    public void setup(){
        consumer = new Consumer(consumerInterface);
    }

    @Test
    public void consumeEvents() {
        // mock event.getPayload()避免空指针
        when(event.getPayload()).thenReturn(new Payload()); // 替换为实际Payload类型或mock对象

        // 拦截EventA的构造过程,返回mock并配置方法行为
        try (MockedConstruction<EventA> mockedEventA = mockConstruction(EventA.class,
            (mock, context) -> {
                when(mock.extractModel()).thenReturn(eventPayload);
            })) {

            consumer.consumeEvents(event);

            verify(consumerInterface, times(1)).handleEvent(eventPayload);
        }
    }
}

注意事项:

  • 使用该方案需要添加mockito-inline依赖(Maven可在pom.xml中引入对应依赖);
  • 这种方式属于低侵入但测试耦合度较高的方案,优先推荐方案一的重构方式,让代码本身具备更好的可测试性。

内容的提问来源于stack exchange,提问作者Smaillns

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 13:33:32