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

Spring Boot中JPAStreamer的JUnit测试出现NullPointerException如何解决

问题原因分析

你遇到的NullPointerException根源在于:你试图用Mockito对普通Java Stream实例进行方法存根,但Mockito只能对它自己生成的Mock对象进行存根操作。你代码里的personStreamMock是personList.stream()生成的真实Stream,不是Mockito Mock出来的对象,所以调用Mockito.when(personStreamMock.filter(...))时会抛出空指针异常。

另外要注意,JPAStreamer返回的Stream实际上是JPAStream<T>(它扩展了Java标准Stream<T>),并且其filter方法接收的是Querydsl的Predicate(比如Person$.age.equal(age)),而非Java Stream的Predicate,这也是原测试逻辑的一个误区。

解决方案

改用Mockito生成JPAStream<Person>的Mock对象,对JPAStreamer的调用链进行完整存根:

修改后的测试类代码

class PersonServiceTest extends BaseServiceTest {

    @InjectMocks
    private PersonService personService;

    @Mock
    private JPAStreamer jpaStreamer;
    // Mock JPAStreamer返回的JPAStream对象
    @Mock
    private JPAStream<Person> jpaStreamMock;
    // Mock过滤后的JPAStream对象
    @Mock
    private JPAStream<Person> filteredJpaStreamMock;

    private List<Person> personList;

    @BeforeEach
    public void setup() {
        personList = Arrays.asList(
                Person.builder().id(1L).firstName("John").lastName("Doe").age(33).birthday(LocalDate.of(1990, 5, 15)).build(),
                Person.builder().id(2L).firstName("Jane").lastName("Smith").age(27).birthday(LocalDate.of(1996, 9, 20)).build(),
                Person.builder().id(3L).firstName("David").lastName("Johnson").age(32).birthday(LocalDate.of(1991, 9, 20)).build()
        );
    }

    @Test
    public void testJpaStreamerFindByAge() {
        // given
        Integer targetAge = 32;
        List<Person> expectedList = personList.stream()
                .filter(person -> person.getAge().equals(targetAge))
                .collect(Collectors.toList());

        // when - 存根JPAStreamer调用链
        Mockito.when(jpaStreamer.stream(Person.class)).thenReturn(jpaStreamMock);
        // 匹配真实的Predicate参数,而非any(),测试更精准
        Mockito.when(jpaStreamMock.filter(Person$.age.equal(targetAge))).thenReturn(filteredJpaStreamMock);
        Mockito.when(filteredJpaStreamMock.collect(Collectors.toList())).thenReturn(expectedList);

        // then
        List<Person> actualPersons = personService.findByAgeForJpaStreamer(targetAge);

        // 断言结果
        assertEquals(expectedList, actualPersons);
        assertEquals(expectedList.size(), actualPersons.size());

        // 断言每个属性
        for (int i = 0; i < expectedList.size(); i++) {
            Person expected = expectedList.get(i);
            Person actual = actualPersons.get(i);
            assertEquals(expected.getId(), actual.getId());
            assertEquals(expected.getFirstName(), actual.getFirstName());
            assertEquals(expected.getLastName(), actual.getLastName());
            assertEquals(expected.getAge(), actual.getAge());
            assertEquals(expected.getBirthday(), actual.getBirthday());
        }

        // 验证方法调用次数
        Mockito.verify(jpaStreamer, Mockito.times(1)).stream(Person.class);
        Mockito.verify(jpaStreamMock, Mockito.times(1)).filter(Person$.age.equal(targetAge));
        Mockito.verify(filteredJpaStreamMock, Mockito.times(1)).collect(Collectors.toList());
    }
}

关键修改点

  1. Mock正确的对象类型:创建JPAStream<Person>的Mock实例,而非使用真实Java Stream。
  2. 精准匹配方法参数:存根filter方法时,匹配业务代码中实际使用的Person$.age.equal(targetAge)Predicate,避免使用any()导致的匹配模糊。
  3. 完整存根调用链:对jpaStreamer.stream() → filter() → collect()整个调用链的返回值进行存根,确保测试逻辑和业务代码的调用流程一致。

内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:44:58