如何对使用Java NIO Files库的文件操作方法进行单元测试
单元测试方案
这里提供两个可行的测试方案,优先推荐JimFS内存文件系统方案,更贴近真实执行逻辑,测试结果可信度更高。
前置优化建议
先对原方法做两个小优化,既提升代码健壮性也更便于测试:
- 调整空指针判断顺序:把
target != null的判断放到Files.notExists(target)前面,避免target为null时触发空指针异常 - 建议将
path1、path2、fileToRead三个变量通过方法参数或者类构造方法注入,不要硬编码在类中,方便测试时传入自定义路径
方案1:JimFS内存文件系统测试(推荐)
JimFS是Google开源的内存文件系统,不需要操作本地磁盘,执行速度快,能真实模拟文件操作的全流程,可直接验证方法执行结果是否符合预期。
依赖引入
Maven坐标:
<dependency> <groupId>com.google.jimfs</groupId> <artifactId>jimfs</artifactId> <version>1.3</version> <scope>test</scope> </dependency>
测试代码示例(JUnit5)
import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import static org.junit.jupiter.api.Assertions.*; public class FileUtilTest { private FileSystem jimfs; private Path sourcePath; private Path targetPath; private final String testListFileName = "file_list.txt"; private final List<String> testFileList = List.of("a.txt", "b.jpg", "c.json"); @BeforeEach void initTestEnv() throws Exception { // 初始化内存文件系统 jimfs = Jimfs.newFileSystem(Configuration.unix()); sourcePath = jimfs.getPath("/test/source"); targetPath = jimfs.getPath("/test/target"); Files.createDirectories(sourcePath); // 构造源目录测试数据 // 1. 写入待读取的文件列表 Files.write(sourcePath.resolve(testListFileName), testFileList, StandardCharsets.UTF_8); // 2. 创建列表内的所有测试文件,写入测试内容 for (String fileName : testFileList) { Files.writeString(sourcePath.resolve(fileName), "test content for " + fileName); } } @AfterEach void clearTestEnv() throws Exception { jimfs.close(); } @Test void testCopyAndMoveFiles() throws Exception { // 初始化业务类,注入测试用的路径参数 YourService yourService = new YourService(); yourService.setPath1(sourcePath.toString()); yourService.setPath2(targetPath.toString()); yourService.setFileToRead(testListFileName); // 执行待测试方法 yourService.copyAndMoveFiles(); // 断言验证执行结果 // 1. 目标目录已成功创建 assertTrue(Files.exists(targetPath)); // 2. 所有列表内的文件都已复制到目标目录,且内容和源文件一致 for (String fileName : testFileList) { Path targetFile = targetPath.resolve(fileName); assertTrue(Files.exists(targetFile)); assertEquals("test content for " + fileName, Files.readString(targetFile)); } } }
方案2:Mockito静态方法Mock(行为验证)
如果不想引入额外依赖,使用Mockito 3.4及以上版本支持的静态类Mock能力,验证Files相关方法的调用行为是否符合预期即可。
测试代码示例
import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.List; import static org.mockito.Mockito.*; public class FileUtilTest { private final String path1 = "/test/source"; private final String path2 = "/test/target"; private final String testListFileName = "file_list.txt"; private final List<String> testFileList = List.of("a.txt", "b.jpg"); @Test void testCopyAndMoveFiles() throws Exception { try (MockedStatic<Files> mockedFiles = mockStatic(Files.class); MockedStatic<Paths> mockedPaths = mockStatic(Paths.class)) { Path source = mock(Path.class); Path target = mock(Path.class); Path listFile = mock(Path.class); // 打桩静态方法返回值 mockedPaths.when(() -> Paths.get(path1)).thenReturn(source); mockedPaths.when(() -> Paths.get(path2)).thenReturn(target); mockedFiles.when(() -> Files.notExists(target)).thenReturn(true); when(source.resolve(testListFileName)).thenReturn(listFile); mockedFiles.when(() -> Files.readAllLines(listFile)).thenReturn(testFileList); // 初始化业务类,设置参数 YourService yourService = new YourService(); yourService.setPath1(path1); yourService.setPath2(path2); yourService.setFileToRead(testListFileName); // 执行待测试方法 yourService.copyAndMoveFiles(); // 验证方法调用行为符合预期 // 验证目标目录创建方法被调用1次 mockedFiles.verify(() -> Files.createDirectories(any()), times(1)); // 验证每个文件都执行了复制操作 for (String fileName : testFileList) { Path sourceFile = mock(Path.class); Path targetFile = mock(Path.class); when(source.resolve(fileName)).thenReturn(sourceFile); when(target.resolve(fileName)).thenReturn(targetFile); mockedFiles.verify(() -> Files.copy(sourceFile, targetFile, StandardCopyOption.REPLACE_EXISTING), times(1)); } } } }
内容的提问来源于stack exchange,提问作者stackerstack
相关产品推荐
相关产品推荐

