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

非构造注入方式下Mock Service的JUnit测试问题解决

解决字段注入下的Spring服务Mock测试问题

我明白你的痛点——用字段注入(@Autowired直接标注在字段上)时,Mockito默认不会自动帮你注入Mock对象,所以才会出现riskFilterService始终为null的情况。不用改成构造注入的话,有两种常用的解决方式,我给你详细说明:

方法一:使用Mockito的@Mock和@InjectMocks注解

这是最常用的方式,通过注解让Mockito自动完成Mock依赖的注入:

  1. 在测试类中,给需要Mock的依赖(RiskFilterService)标注@Mock注解
  2. 给待测试的服务类(BinCountryCheckFilterImpl)标注@InjectMocks注解
  3. 在测试方法执行前,调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:36:00