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

如何Mock Scanner与File类?测试sendMailWitchChanges方法时出现空指针异常的解决方案

解决Mockito模拟Scanner构造函数时的NullPointerException问题

我太懂你这个坑了!你遇到的NullPointerException是因为默认的Mockito核心库不支持直接模拟构造函数调用——也就是你写的when(new Scanner(any(File.class))).thenReturn(scannerMock)这行,Mockito没法直接拦截new Scanner()的构造行为,所以才会抛出空指针。

问题根源

Mockito默认只能模拟对象的方法调用,要拦截并模拟构造函数,需要用到mockito-inline扩展(Mockito 3.4+版本开始支持),它专门用来处理构造函数、静态方法这类原本Mockito核心不支持的场景。

正确的测试代码写法

首先,确保你的项目依赖里引入了mockito-inline:

  • 如果是Maven,在pom.xml里添加:
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>4.11.0</version> <!-- 用最新稳定版即可 -->
    <scope>test</scope>
</dependency>
  • 如果是Gradle,在build.gradle里添加:
testImplementation 'org.mockito:mockito-inline:4.11.0'

然后修改你的测试代码,用Mockito.mockConstruction()来模拟Scanner的构造过程:

@Test 
void sendMailWitchChanges() throws MessagingException, IOException { 
    // 用mockConstruction模拟Scanner的构造,捕获创建的实例并设置行为
    try (MockedConstruction<Scanner> mockedScanner = Mockito.mockConstruction(Scanner.class, 
        (mock, context) -> {
            // 处理Scanner的链式调用:useDelimiter返回自身
            when(mock.useDelimiter(anyString())).thenReturn(mock);
            when(mock.next()).thenReturn("afterChanges");
        })) {

        LocalDateTime now = LocalDateTime.now(); 
        UserEntity user = new UserEntity(); 
        JobEntity job = new JobEntity("uuid", now, 1, Set.of(user)); 
        DirectoryEntity directory = new DirectoryEntity(1, "directory"); 
        FileIdFilePathFileContentEntity fileEntity = new FileIdFilePathFileContentEntity(1, "fileName", "fileContent"); 

        mailService.sendMailWitchChanges(job, directory, fileEntity, now, "fileName");

        // 验证目标方法被调用一次
        Mockito.verify(mailService, times(1))
            .sendMailWitchChanges(job, directory, fileEntity, now, "fileName");
        
        // 可选:验证Scanner的构造和方法调用是否符合预期
        List<Scanner> constructedScanners = mockedScanner.constructed();
        assertThat(constructedScanners).hasSize(1);
        verify(constructedScanners.get(0)).useDelimiter("\\Z");
        verify(constructedScanners.get(0)).next();
    }
}

关键修改点解释

  1. MockedConstruction:这是mockito-inline提供的API,用来拦截指定类的构造函数调用,所有new Scanner(...)都会返回我们的mock实例。
  2. 资源管理:用try-with-resources包裹,确保mock构造的资源在测试结束后自动清理,避免影响其他测试用例。
  3. 链式调用处理:Scanner的useDelimiter()返回自身,所以必须设置when(mock.useDelimiter(anyString())).thenReturn(mock),否则链式调用scanner.useDelimiter(...).next()会抛出空指针。
  4. 额外验证(可选):通过mockedScanner.constructed()获取所有构造的mock实例,验证方法调用细节,让测试逻辑更严谨。

另外,你原来代码里verificationData -> times(1)的写法没必要,直接用times(1)就可以,我已经帮你修正啦。

内容的提问来源于stack exchange,提问作者Emanuelo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 17:47:48