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

Mockito测试AccountDetails类时构造器注入对象为空的解决方案(不使用PowerMock)

问题根源分析

你遇到的问题核心是测试类里的依赖注入方式冲突:一方面用了@InjectMocks注解,另一方面又手动实例化被测对象并硬传部分依赖,导致Mockito无法正确初始化所有Mock对象。具体来说,你自定义的AccountDetailsTestImpl构造器只接收ConstantMapService,传给父类时直接用测试类里的accountDetailsRestClient,但这个Mock此时可能还没被Mockito完成初始化,加上@InjectMocks和手动new的逻辑冲突,就出现了要么这个依赖空、要么那个依赖空的情况。

解决方案(无需PowerMock)

下面提供两种可行的修改方案,你可以根据自己的需求选择:

方案一:完全使用Mockito注解自动注入(推荐)

去掉自定义子类,让Mockito自动处理依赖注入和实例创建,代码更简洁规范:

@RunWith(MockitoJUnitRunner.class)
public class AccountDetailsTest {
    @Mock
    private AccountDetailsRestClient accountDetailsRestClient;
    
    @Mock
    private ConstantMapService constantMapService;
    
    @Mock(answer = RETURNS_DEEP_STUBS)
    private CalculationMap calculationMap;

    @InjectMocks
    private AccountDetails sut; // Mockito自动创建实例并注入所有Mock

    @Before
    public void setUp() {
        // Mock constantMapService的行为,确保构造器调用initializeConstantsData时返回我们的mock对象
        when(constantMapService.getMap(any(LocalDate.class))).thenReturn(calculationMap);
    }

    @Test
    public void testMethod() {
        // 现在所有依赖都已正确初始化,可以编写测试逻辑
        assertNotNull(sut);
        assertNotNull(accountDetailsRestClient);
        assertNotNull(constantMapService);
        assertNotNull(calculationMap);
    }
}

方案说明:

  • @InjectMocks会自动创建AccountDetails的实例,并把所有标记了@Mock的依赖注入进去;
  • 在setUp方法中提前MockconstantMapService.getMap()的返回值,确保构造器里的initializeConstantsData方法能拿到我们预设的calculationMapMock对象。

方案二:保留自定义子类(若需扩展被测类行为)

如果你因为某些业务需求必须保留自定义的AccountDetailsTestImpl,可以调整测试类的初始化逻辑,手动传入所有Mock依赖:

@RunWith(MockitoJUnitRunner.class)
public class AccountDetailsTest {
    @Mock
    private AccountDetailsRestClient accountDetailsRestClient;
    
    @Mock
    private ConstantMapService constantMapService;
    
    @Mock(answer = RETURNS_DEEP_STUBS)
    private CalculationMap calculationMap;

    private AccountDetails sut;

    @Before
    public void setUp() {
        when(constantMapService.getMap(any(LocalDate.class))).thenReturn(calculationMap);
        // 手动创建被测实例,传入所有Mock依赖
        sut = new AccountDetailsTestImpl(accountDetailsRestClient, constantMapService);
    }

    @Test
    public void testMethod() {
        assertNotNull(sut);
        assertNotNull(accountDetailsRestClient);
        assertNotNull(constantMapService);
        assertNotNull(calculationMap);
    }

    class AccountDetailsTestImpl extends AccountDetails {
        // 修改子类构造器,接收所有父类需要的依赖
        AccountDetailsTestImpl(final AccountDetailsRestClient accountDetailsRestClient, final ConstantMapService constantMapService) {
            super(accountDetailsRestClient, constantMapService);
        }
    }
}

方案说明:

  • 去掉@InjectMocks,在setUp里手动构建被测实例,把所有Mock依赖都传入;
  • 同样需要提前MockconstantMapService的行为,确保calculationMap被正确初始化。
额外小建议(业务类优化)

你的业务类initializeConstantsData方法当前的判空逻辑在单线程场景下没问题,如果是并发场景,可以考虑改成双重检查锁,避免多线程下重复初始化:

private void initializeConstantsData() {
    if (this.calculationMap == null) {
        synchronized (this) {
            if (this.calculationMap == null) {
                this.calculationMap = constantMapService.getMap(java.time.LocalDate.now());
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 21:17:34