使用mockito-inline的MockedConstruction模拟FileInputStream报初始化错误如何解决
异常产生原因
- 全局Mock范围冲突:
MockedConstruction会拦截作用域内所有FileInputStream的构造调用,包括JVM类加载器自身加载类时触发的FileInputStream实例化。你定义的初始化回调会对这些类加载阶段产生的FileInputStream实例也执行,而此时测试上下文还未完全准备好,直接触发空指针。 - 写法错误:一是打桩
read方法时参数匹配不规范,没有对应read(byte[], int, int)方法的参数类型;二是verify(mocked, times(1)).close()写法完全错误,mocked是构造控制对象,不是FileInputStream实例本身,不能直接调用实例方法验证。 - 不必要的资源依赖:你在打桩逻辑里直接读取本地文件
new File(dir + fileName).length(),如果测试文件不存在,会直接返回0甚至抛出异常,原PowerMock用例直接写死返回值的方案更合理,不需要依赖真实文件。
修复方案
前置条件:确保你引入的是
mockito-inline依赖而非普通mockito-core,版本不低于3.5.0,才能支持构造函数Mock能力。
修复后的测试代码如下:
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.File; import java.io.FileInputStream; import org.junit.Test; import org.mockito.MockedConstruction; import org.mockito.Mockito; public class FileViewerMockitoTest { String dir = System.getProperty("user.dir"); String fileName = "/testFiles/testRead.txt"; @Test public void testFileViewerWithFilename() throws Exception { try (MockedConstruction<FileInputStream> mocked = Mockito.mockConstruction(FileInputStream.class, (mock, context) -> { // 过滤类加载器触发的FileInputStream构造,只对业务代码的构造打桩 if (context.arguments().size() > 0 && context.arguments().get(0) instanceof File) { when(mock.read(any(byte[].class), anyInt(), anyInt())).thenReturn(2000); } })) { FileViewer cut = new FileViewer(dir + fileName); // 从构造的实例列表中获取mock对象再做验证 FileInputStream mockStream = mocked.constructed().get(0); verify(mockStream, times(1)).close(); cut.dispose(); } } }
替代实现方案
如果JDK核心类Mock的冲突始终无法解决,可以选以下两种方案:
- 临时文件方案:不Mock
FileInputStream,直接在测试用例执行前创建临时测试文件写入测试内容,测试完成后自动删除,完全走真实逻辑,避免Mock冲突。 - 轻量重构方案:给
FileViewer增加包级私有构造,把FileInputStream的创建逻辑抽离出来,测试的时候直接传入Mock的FileInputStream实例,不需要Mock构造函数,从根源上避免Mock JDK核心类的问题。
内容的提问来源于stack exchange,提问作者Amgad Hanafy
相关产品推荐
相关产品推荐

