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()); } }
关键修改点
- Mock正确的对象类型:创建
JPAStream<Person>的Mock实例,而非使用真实Java Stream。 - 精准匹配方法参数:存根
filter方法时,匹配业务代码中实际使用的Person$.age.equal(targetAge)Predicate,避免使用any()导致的匹配模糊。 - 完整存根调用链:对
jpaStreamer.stream()→filter()→collect()整个调用链的返回值进行存根,确保测试逻辑和业务代码的调用流程一致。
内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu
相关产品推荐
相关产品推荐

