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依赖都传入; - 同样需要提前Mock
constantMapService的行为,确保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
相关产品推荐
相关产品推荐

