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

Java测试中Mockito模拟PromoCodeService遇空指针异常的解决咨询

问题分析与解决方案

你遇到的promoCodeService为null的问题,核心是Mock对象没有正确注入到BusterClient实例中,同时你手动通过无参构造创建BusterClient,导致依赖的服务无法被初始化。以下是正确的Mock和测试方式:

修正后的测试代码(JUnit 5示例)

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.lang.reflect.Method;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
public class BusterClientTest {

    // 用@Mock注解创建PromoCodeService的Mock实例
    @Mock
    private PromoCodeService promoCodeService;

    // 用@InjectMocks让Mockito自动将Mock注入到BusterClient中
    // 无需手动new实例,Mockito会通过带参构造完成注入
    @InjectMocks
    private BusterClient busterClient;

    @Test
    public void testTrackedReferralNotMatchingPromoCode() throws Exception {
        // 1. 配置Mock的返回值
        when(promoCodeService.findByCampaignPromoCodeIds(12L, "test"))
                .thenReturn(Optional.of(PromoCode.builder().id(1L).build()));

        // 2. 准备测试用的CustomerRegistrationEvent实例(Mock必要方法)
        CustomerRegistrationEvent event = mock(CustomerRegistrationEvent.class);
        when(event.hasPromoCode()).thenReturn(true);
        when(event.getPromoCode()).thenReturn("test");
        // 确保getCampaignIdAsLong方法能从event拿到12L(根据实际实现调整)
        when(event.getCampaignId()).thenReturn("12");

        // 3. 反射调用私有方法
        Method method = BusterClient.class.getDeclaredMethod("getPromoCode", CustomerRegistrationEvent.class);
        method.setAccessible(true);
        Optional<PromoCode> result = (Optional<PromoCode>) method.invoke(busterClient, event);

        // 4. 验证结果与Mock调用
        assertTrue(result.isPresent());
        assertEquals(1L, result.get().getId());
        verify(promoCodeService).findByCampaignPromoCodeIds(12L, "test");
    }
}

关键修正点说明

  1. 启用Mockito注解支持

    • JUnit 5用@ExtendWith(MockitoExtension.class),JUnit 4用@RunWith(MockitoJUnitRunner.class),确保Mockito能处理@Mock和@InjectMocks注解。
  2. 正确注入Mock对象

    • 用@Mock声明PromoCodeService的Mock实例,替代手动mock(PromoCodeService.class)。
    • @InjectMocks无需手动创建BusterClient实例,Mockito会自动调用带参构造BusterClient(PromoCodeService),将Mock注入进去。
  3. 完善测试前置条件

    • 必须MockCustomerRegistrationEvent的必要方法(如hasPromoCode()、getPromoCode()),确保私有方法getPromoCode能走到调用promoCodeService的分支。

额外建议

  • 尽量避免直接测试私有方法,建议测试调用该私有方法的公有业务方法,更符合黑盒测试的设计原则。
  • 如果必须测试私有方法,可使用PowerMock的Whitebox.invokeMethod简化反射代码,减少样板代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 13:50:25