【问题标题】:NPE when Java method is called second time after mockupNPE在Java方法被调用后,实体模型第二次
【发布时间】:2020-03-05 01:24:57
【问题描述】:

我想实现 JUnit 5 测试以测试服务:

JPA 存储库:

@Repository
public interface RiskFilterRepository extends JpaRepository<RiskFilters, Integer> {
    .....
}

服务接口:

public interface RiskFilterService {
    ......
}

服务实现:

@Service
@Qualifier("riskFilterService")
@Transactional
public class RiskFilterServiceImpl implements RiskFilterService {

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private RiskFilterRepository dao;
    ...
}

测试服务:

@Service
public class BinCountryCheckFilterImpl {

    @Autowired
    @Qualifier("riskFilterService")
    private RiskFilterService riskFilterService;

    public BinCountryCheckFilterImpl(Terminals terminal) {  
       this.terminal = terminal;
    }

    public void validateBinCountryCheckFilter() throws JAXBException, JsonProcessingException {

        List<RiskFilters> filter_list = riskFilterService.findRiskFiltersByTerminalIdAndType(terminal.getId(), "BinCountryCheckFilter");
    }
}

JUnit 测试

    public class BinCountryCheckFilterImplTest    {

        @Autowired
        private RiskFilterRepository repository;

        @BeforeEach
        public void beforeEachTest() throws IOException {
            MockitoAnnotations.initMocks(this); 
        }

        @Test
        public void testBinCountryCheckFilterImpl() throws JsonProcessingException, JAXBException {
           // this mock is working fine
           BDDMockito.given(riskFilterService.findRiskFiltersByTerminalIdAndType(anyInt(), anyString())).willReturn(riskFiltersList);

           // this mock is working fine - I don't get NPE
           List<RiskFilters> filter_list = riskFilterService.findRiskFiltersByTerminalIdAndType(11, "BinCountryCheckFilter");

           // this mock is working fine
           BinCountryCheckFilterImpl binCountryCheckFilterImpl = new BinCountryCheckFilterImpl(terminal);

           // I get NPE here 
           binCountryCheckFilterImpl.validateBinCountryCheckFilter();
        }    
    }

当我为此 Java 方法 validateBinCountryCheckFilter() 运行 JUnit 代码时出现 NPE 错误。

List<RiskFilters> filter_list = riskFilterService.findRiskFiltersByTerminalIdAndType(11, "BinCountryCheckFilter");

所以当我第一次使用BDDMockito.given.... 运行代码时,模拟的SQL 查询riskFilterService.findRiskFiltersByTerminalIdAndType.... 工作正常。 但是在第二次运行binCountryCheckFilterImpl.validateBinCountryCheckFilter(); 之后,我得到了代码riskFilterService.findRiskFiltersByTerminalIdAndType.... 的NPE

你知道我该如何解决这个问题吗?

【问题讨论】:

  • 我不确定 BDDMockito,但在某些框架中,您需要分别模拟连续调用的结果:.willReturn(riskFiltersList).willReturn(riskFiltersList).
  • 很可能被测对象没有使用模拟。而且由于没有分配依赖关系,因此您将获得 NPE。为什么不像终端一样将其显式注入主题。
  • 模拟在哪里?您在测试中从哪里获得 riskFilterService ?为什么不通过构造函数注入依赖项?
  • 确实,@Nkosi 说了什么...BinCountryCheckFilterImpl binCountryCheckFilterImpl = new BinCountryCheckFilterImpl(terminal); 中的terminal 是在哪里定义的?
  • @ZeroOne Terminal 它只是一个 Java 对象。

标签: java spring spring-boot junit junit5


【解决方案1】:

很可能被测对象没有使用模拟。而且由于没有分配依赖关系,因此您将获得 NPE。

为什么不像终端一样通过构造函数注入将其显式注入主题。

@Service
public class BinCountryCheckFilterImpl {

    private RiskFilterService riskFilterService;
    private Terminals terminal;

    @Autowired
    public BinCountryCheckFilterImpl(Terminals terminal, RiskFilterService riskFilterService) {  
        this.terminal = terminal;
        this.riskFilterService = riskFilterService;
    }

    public void validateBinCountryCheckFilter() throws JAXBException, JsonProcessingException {

        List<RiskFilters> filter_list = riskFilterService.findRiskFiltersByTerminalIdAndType(terminal.getId(), "BinCountryCheckFilter");
    }
}

这样可以在测试时显式注入模拟

public class BinCountryCheckFilterImplTest    {

    @Test
    public void testBinCountryCheckFilterImpl() throws JsonProcessingException, JAXBException {
        //Arrange
        RiskFilterService riskFilterService = Mockito.mock(RiskFilterService.class);
        Terminals terminal = Mockito.mock(Terminals.class);

        BDDMockito.given(
            riskFilterService.findRiskFiltersByTerminalIdAndType(anyInt(), anyString()))
            .willReturn(riskFiltersList);

        int expectedInt = 11;
        String expectedString = "BinCountryCheckFilter";

        when(terminal.getId()).thenReturn(expectedInt);

        BinCountryCheckFilterImpl binCountryCheckFilterImpl = 
            new BinCountryCheckFilterImpl(terminal, riskFilterService);

        //Act
        binCountryCheckFilterImpl.validateBinCountryCheckFilter();

        //Assert
        then(riskFilterService)
          .should()
          .findRiskFiltersByTerminalIdAndType(expectedInt, expectedString);
    }    
}

【讨论】:

  • 它正在工作,但有一种方法我不想将RiskFilterService 作为构造函数的参数发送,以保持服务的设计干净。还有其他方法吗?
  • @PeterPenzov 你应该如何提供你不能注入它们的模拟?
  • 好问题 - 我不知道。其他想法?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-10
  • 2020-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多