使用 junit 5 的 `@tempdir` 自动生成隔离临时目录,确保每次测试都在干净、独立的文件环境中运行,避免因文件重命名或删除导致的测试污染和非幂等问题。
在单元测试中验证涉及真实文件系统操作(如 Files.move() 和 Files.delete())的方法时,一个核心挑战是测试的幂等性(idempotence):即同一测试用例重复执行应始终产生一致、可预测的结果。而原始 loadFile(Path) 方法的行为具有副作用——它要么删除输入文件,要么将其重命名为带 ERROR_ 前缀的新路径。若直接复用固定路径(如 src/test/resources/test.txt),首次运行后文件状态改变,后续运行必然失败,严重违背测试可重复执行的基本原则。
解决该问题的关键在于为每次测试提供隔离、自动清理的文件环境。JUnit 5.4+ 引入的 @TempDir 参数化扩展正是为此场景量身定制:
- @TempDir 会在测试方法执行前自动创建一个唯一的临时目录;
- 该目录及其所有内容(包括子文件、子目录)将在测试方法退出后由 JUnit 自动递归清理;
- 每个测试方法获得独立的 Path 实例,天然实现测试间隔离。
以下为推荐的测试实现方式:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.*;
class FileLoaderTest {
@Test
void loadsFileCorrectly(@TempDir Path temp) throws Exception {
Path file = temp.resolve("data.txt");
Files.createFile(file); // 准备正常输入文件
// 执行被测方法(假设已注入或实例化 loader)
new FileLoader().loadFile(file);
// 验证文件已被删除
assertFalse(Files.exists(file), "Expected file to be deleted on success");
}
@Test
void loadsFileWithException(@TempDir Path temp) throws Exception {
Path file = temp.resolve("faulty.txt");
Files.createFile(file);
// 模拟 RuntimeException(例如通过 Mockito 注入异常行为,或使用测试桩)
// 此处假设 loadFile 已被改造为可注入异常逻辑,或通过其他方式触发异常分支
// 实际中建议将异常触发点解耦(如依赖抛出异常的策略对象),便于精准控制
// 示例:若使用 Mockito 模拟底层 I/O 行为,可在此处配置异常
// 如无法修改原方法,则需结合 SecurityManager 或字节码工具(不推荐),故更优解是重构可测试性
// 假设异常已触发
new FileLoader().loadFile(file);
// 验证错误重命名是否发生
Path errorPath = file.resolveSibling("ERROR_" + file.getFileName());
assertTrue(Files.exists(errorPath), "Expected file to be renamed with ERROR_ prefix");
assertFalse(Files.exists(file), "Original file should no longer exist");
}
}
⚠️ 重要注意事项: