非构造注入方式下Mock Service的JUnit测试问题解决
解决字段注入下的Spring服务Mock测试问题
我明白你的痛点——用字段注入(@Autowired直接标注在字段上)时,Mockito默认不会自动帮你注入Mock对象,所以才会出现riskFilterService始终为null的情况。不用改成构造注入的话,有两种常用的解决方式,我给你详细说明:
方法一:使用Mockito的@Mock和@InjectMocks注解
这是最常用的方式,通过注解让Mockito自动完成Mock依赖的注入:
- 在测试类中,给需要Mock的依赖(
RiskFilterService)标注@Mock注解 - 给待测试的服务类(
BinCountryCheckFilterImpl)标注@InjectMocks注解 - 在测试方法执行前,调用
MockitoAnnotations.openMocks(this)初始化这些注解
对应的测试类代码:
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import static org.mockito.BDDMockito.*; public class BinCountryCheckFilterImplTest { // 标注要Mock的依赖对象 @Mock private RiskFilterService riskFilterService; // 标注待测试的服务,Mockito会自动把@Mock的对象注入到这里的字段中 @InjectMocks private BinCountryCheckFilterImpl binCountryCheckFilterImpl; // 初始化Mockito注解 @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); } @Test public void testValidateBinCountryCheckFilter() throws JAXBException, JsonProcessingException { // Arrange Merchants merchant = mock(Merchants.class); Contracts contract = mock(Contracts.class); Terminals terminal = mock(Terminals.class); PaymentTransaction message = mock(PaymentTransaction.class); HttpServletRequest request = mock(HttpServletRequest.class); int expectedTerminalId = 11; String expectedFilterType = "BinCountryCheckFilter"; given(terminal.getId()).willReturn(expectedTerminalId); given(riskFilterService.findRiskFiltersByTerminalIdAndType(expectedTerminalId, expectedFilterType)) .willReturn(new ArrayList<>()); // 替换成你需要的测试数据 // Act binCountryCheckFilterImpl.validateBinCountryCheckFilter(merchant, contract, terminal, message, request); // Assert then(riskFilterService) .should() .findRiskFiltersByTerminalIdAndType(expectedTerminalId, expectedFilterType); } }
方法二:手动调用Mockito.injectMocks()注入
如果不想用注解,也可以手动初始化Mock对象,然后调用Mockito.injectMocks()把Mock注入到待测试服务的字段中:
import org.junit.jupiter.api.Test; import java.util.ArrayList; import static org.mockito.BDDMockito.*; public class BinCountryCheckFilterImplTest { @Test public void testValidateBinCountryCheckFilter() throws JAXBException, JsonProcessingException { // Arrange RiskFilterService riskFilterService = mock(RiskFilterService.class); BinCountryCheckFilterImpl binCountryCheckFilterImpl = new BinCountryCheckFilterImpl(); // 手动将Mock对象注入到服务的字段中 org.mockito.Mockito.injectMocks(binCountryCheckFilterImpl); Merchants merchant = mock(Merchants.class); Contracts contract = mock(Contracts.class); Terminals terminal = mock(Terminals.class); PaymentTransaction message = mock(PaymentTransaction.class); HttpServletRequest request = mock(HttpServletRequest.class); int expectedTerminalId = 11; String expectedFilterType = "BinCountryCheckFilter"; given(terminal.getId()).willReturn(expectedTerminalId); given(riskFilterService.findRiskFiltersByTerminalIdAndType(expectedTerminalId, expectedFilterType)) .willReturn(new ArrayList<>()); // Act binCountryCheckFilterImpl.validateBinCountryCheckFilter(merchant, contract, terminal, message, request); // Assert then(riskFilterService) .should() .findRiskFiltersByTerminalIdAndType(expectedTerminalId, expectedFilterType); } }
补充说明
两种方式的核心都是让Mockito帮你把Mock对象注入到服务类的私有字段中——因为字段注入没有暴露构造器,所以需要Mockito的额外支持。这两种方案都不需要修改原服务类的代码,完全满足你“不使用构造注入”的要求。
内容的提问来源于stack exchange,提问作者Peter Penzov
相关产品推荐
相关产品推荐

