Временные файлы: @TempDir
Код, который работает с файлами, неудобно тестировать: куда писать, как убирать за собой, что будет, если два теста одновременно пишут один файл? JUnit решает это аннотацией @TempDir. Перед тестом он создаёт пустую временную папку, передаёт тесту её путь, а после теста удаляет её со всем содержимым.
Тестируемый класс
Блог выгружает посты в CSV-файл. Создайте src/main/java/com/example/blog/PostExporter.java:
// src/main/java/com/example/blog/PostExporter.java
package com.example.blog;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
public class PostExporter {
public void exportCsv(List<Post> posts, Path file) throws IOException {
List<String> lines = posts.stream()
.map(p -> p.id() + ";" + p.title() + ";" + p.author())
.toList();
Files.write(file, lines);
}
}Метод получает путь к файлу параметром — именно это делает его удобным для тестов: куда писать, решает тест.
@TempDir на поле
// src/test/java/com/example/blog/PostExporterTest.java
package com.example.blog;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
class PostExporterTest {
@TempDir
Path dir;
@Test
void writesOneLinePerPost() throws IOException {
Path file = dir.resolve("posts.csv");
List<Post> posts = List.of(
new Post(1L, "Привет, JUnit", "anna", true),
new Post(2L, "Основы Mockito", "oleg", false)
);
new PostExporter().exportCsv(posts, file);
assertEquals(List.of("1;Привет, JUnit;anna", "2;Основы Mockito;oleg"),
Files.readAllLines(file));
}
}dir — свежая пустая папка для каждого теста где-то во временном каталоге системы. dir.resolve("posts.csv") строит путь к файлу внутри неё. Тест пишет файл, читает его обратно и сравнивает строки. Files.write и Files.readAllLines по умолчанию используют UTF-8, поэтому русский текст не портится. Тестовый метод может объявить throws IOException — неожиданное исключение просто уронит тест.
@TempDir на параметре
Папку можно запросить и параметром теста:
// src/test/java/com/example/blog/PostExporterTest.java
@Test
void emptyListGivesEmptyFile(@TempDir Path other) throws IOException {
Path file = other.resolve("empty.csv");
new PostExporter().exportCsv(List.of(), file);
assertEquals(0, Files.size(file));
}Это удобно, когда файлы нужны только одному тесту класса.
Почему не просто «C:/tmp»
| Фиксированный путь | @TempDir |
|---|---|
| Работает только на машине, где есть эта папка | Работает везде: Windows, Linux, CI |
| Файлы от прошлого запуска могут повлиять на результат | Каждый тест начинает с пустой папки |
| Два теста, запущенные параллельно, перезаписывают друг друга | У каждого теста своя папка |
| Мусор остаётся на диске | Папка удаляется после теста |
Если после падения нужно посмотреть на файл, напишите @TempDir(cleanup = CleanupMode.ON_SUCCESS): папка останется, только когда тест упал.
Частая ошибка
@TempDir работает только с java.nio.file.Path и java.io.File. Поле типа String — ошибка конфигурации:
@TempDir
String dir;org.junit.jupiter.api.extension.ExtensionConfigurationException: Can only resolve @TempDir field of type java.nio.file.Path or java.io.File but was: java.lang.StringОбъявите поле как Path. Если нужна строка, вызовите dir.toString().