1. 概述
在编写单元测试时,我们经常需要使用临时文件。但手动创建、管理以及删除这些文件会带来一定负担。
JUnit 5 自 5.4.2 版本起引入了 TempDirectory 扩展,为我们提供了一种简单、统一的方式来处理临时目录的创建与清理。本文将带你快速了解如何在 JUnit 5 中使用 @TempDir
注解来简化临时文件操作。
2. TempDirectory 扩展简介
JUnit 5 提供的 TempDirectory 扩展是一个内置的、默认注册的扩展组件,无需额外使用 @ExtendWith(TempDirectory.class)
注解即可使用。
它支持在测试方法或测试类级别创建临时目录,并在测试结束后自动清理。需要注意的是,该功能在 JUnit 5 中仍处于 实验性(EXPERIMENTAL) 阶段,鼓励用户反馈使用体验。
3. Maven 依赖配置
在使用 TempDirectory 功能前,需要引入以下 JUnit 5 的 Maven 依赖:
JUnit 核心 API:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
参数化测试依赖(可选):
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
最新版本可在 Maven Central 查找。
4. 使用 @TempDir
注解
JUnit 5 提供了 @TempDir
注解用于注入临时目录。目前只支持两种类型:
java.nio.file.Path
java.io.File
如果使用其他类型会抛出 ParameterResolutionException
。
下面我们来看几种典型的使用方式。
4.1. 作为测试方法参数
你可以将 @TempDir
用作测试方法的参数,JUnit 会自动注入一个临时目录:
@Test
void givenTestMethodWithTempDirectory_whenWriteToFile_thenContentIsCorrect(@TempDir Path tempDir)
throws IOException {
Path numbers = tempDir.resolve("numbers.txt");
List<String> lines = Arrays.asList("1", "2", "3");
Files.write(numbers, lines);
assertAll(
() -> assertTrue("File should exist", Files.exists(numbers)),
() -> assertLinesMatch(lines, Files.readAllLines(numbers)));
}
✅ 这个测试方法会创建一个临时文件并写入内容,然后验证文件是否存在以及内容是否正确。
4.2. 作为类成员变量
你也可以将 @TempDir
注解用于类的字段,JUnit 会在整个测试类中为其创建临时目录:
@TempDir
File anotherTempDir;
@Test
void givenFieldWithTempDirectoryFile_whenWriteToFile_thenContentIsCorrect() throws IOException {
assertTrue("Should be a directory", this.anotherTempDir.isDirectory());
File letters = new File(anotherTempDir, "letters.txt");
List<String> lines = Arrays.asList("x", "y", "z");
Files.write(letters.toPath(), lines);
assertAll(
() -> assertTrue("File should exist", Files.exists(letters.toPath())),
() -> assertLinesMatch(lines, Files.readAllLines(letters.toPath())));
}
⚠️ 注意:如果多个测试方法共用同一个字段,每个测试方法都会获得一个独立的临时目录实例。
4.3. 共享临时目录
如果你希望多个测试方法共享同一个临时目录,可以将字段声明为 static
:
@TempDir
static Path sharedTempDir;
@Test
@Order(1)
void givenFieldWithSharedTempDirectoryPath_whenWriteToFile_thenContentIsCorrect() throws IOException {
Path numbers = sharedTempDir.resolve("numbers.txt");
List<String> lines = Arrays.asList("1", "2", "3");
Files.write(numbers, lines);
assertAll(
() -> assertTrue("File should exist", Files.exists(numbers)),
() -> assertLinesMatch(lines, Files.readAllLines(numbers)));
}
@Test
@Order(2)
void givenAlreadyWrittenToSharedFile_whenCheckContents_thenContentIsCorrect() throws IOException {
Path numbers = sharedTempDir.resolve("numbers.txt");
assertLinesMatch(Arrays.asList("1", "2", "3"), Files.readAllLines(numbers));
}
✅ 使用 @Order
可以控制测试方法的执行顺序,确保测试逻辑顺序执行。
4.4. 控制目录清理行为
JUnit 默认会在测试方法执行完成后自动删除临时目录。但你可以通过 cleanup
参数控制清理行为:
@Test
@Order(1)
void whenTestMethodWithTempDirNeverCleanup_thenSetInstanceVariable(@TempDir(cleanup = NEVER) Path tempDir) {
theTempDirToBeChecked = tempDir;
System.out.println(tempDir.toFile().getAbsolutePath());
}
@Test
@Order(2)
void whenTestMethodWithTempDirNeverCleanup_thenTempDirShouldNotBeRemoved() {
assertNotNull(theTempDirToBeChecked);
assertTrue(theTempDirToBeChecked.toFile().isDirectory());
}
清理模式包括:
ALWAYS
:总是清理(默认)ON_SUCCESS
:仅在测试成功时清理NEVER
:从不清理
⚠️ 使用 NEVER
模式时,需配合 @TestInstance(PER_CLASS)
使用,否则实例字段在后续测试中不可用。
5. 使用细节与注意事项
5.1. 临时目录创建位置
JUnit 使用 Files.createTempDirectory(String prefix)
创建临时目录,默认使用系统临时目录(由环境变量 TMPDIR
指定)。
例如:
TMPDIR=/var/folders/3b/rp7016xn6fz9g0yf5_nj71m00000gn/T/
生成的文件路径类似:
/var/folders/3b/rp7016xn6fz9g0yf5_nj71m00000gn/T/junit5416670701666180307/numbers.txt
如果创建失败,会抛出 ExtensionConfigurationException
或 ParameterResolutionException
。
5.2. 清理失败处理
默认情况下,JUnit 会在测试结束后递归删除临时目录及其内容。如果删除失败,会抛出 IOException
并导致测试失败。
6. 自定义临时目录创建逻辑
如需自定义临时目录的创建逻辑,可以实现 TempDirFactory
接口:
class TempDirFactoryUnitTest {
@Test
void factoryTest(@TempDir(factory = Factory.class) Path tempDir) {
assertTrue(tempDir.getFileName().toString().startsWith("factoryTest"));
}
static class Factory implements TempDirFactory {
@Override
public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)
throws IOException {
return Files.createTempDirectory(extensionContext.getRequiredTestMethod().getName());
}
}
}
✅ 通过实现 createTempDirectory
方法,你可以完全控制临时目录的命名与创建逻辑。
7. 总结
JUnit 5 的 @TempDir
注解为我们提供了一种优雅、简洁的方式来管理测试中的临时文件和目录。它支持多种使用方式,包括:
- 方法参数注入
- 类字段注入
- 多方法共享目录
- 自定义清理策略
- 自定义创建逻辑
使用时需注意:
- 支持类型只有
Path
和File
@TempDir
是实验性功能,未来可能有变化- 清理失败会导致测试失败
- 可通过
TempDirFactory
实现自定义逻辑
完整示例代码可在 GitHub 获取。