Diagnosing and Fixing JUnit 5 @TempDir Cleanup Issues
When JUnit 5 tests leave temporary directories behind, the problem can be traced to annotation placement, file locks, or parallel execution. This guide walks through diagnosing the issue, checking the test setup, and applying fixes that ensure clean‑up after each test.
20 May 2026, 08:59 UTC

Recognizable Problem: Temporary Directories Left Over After Tests
When running JUnit 5 (Jupiter) tests that use the @TempDir annotation, you may notice that temporary directories are not removed after the test finishes. This can clutter the operating‑system temp area, consume disk space, and interfere with subsequent test runs.
Why It Happens
| Condition | Cause | Check | Fix |
|---|---|---|---|
| Directory remains after test method | File handles still open or JVM terminates abnormally | Verify that no background thread holds a file descriptor to the temp directory after the test returns | Close streams, join threads, or avoid passing the path to long‑running tasks |
| Directory shared across test methods | Class‑scoped @TempDir used unintentionally | Inspect annotation placement – method parameter vs class field | Move @TempDir to a method parameter for per‑test isolation |
| Parallel test execution corrupts shared temp directory | Concurrent tests write to the same directory without unique subfolders | Run tests sequentially or generate a unique subdirectory name inside the shared temp dir | Use @TempDir with @BeforeEach to create per‑test subfolders |
| Operating‑system temp location not cleaned | Implementation‑defined parent temp location contains other files | Check the value of java.io.tmpdir before test runs | Do not rely on a fixed path; let JUnit create the directory and use the injected Path |
Ordered Checks
- Confirm JUnit Version –
junit-jupiter-engine5.4+ is required for stable@TempDirsupport. Runmvn dependency:tree | grep junit-jupiter-engineor check Gradle’sdependenciesblock. - Check Annotation Placement – If you see a field annotated with
@TempDir, the directory is class‑scoped. For per‑test isolation, use a method parameter:void testMethod(@TempDir Path tempDir). - Verify Directory Creation – Inside the test, assert
Files.exists(tempDir)and logtempDir.toString(). This confirms the framework created the directory before execution. - Inspect for Open Handles – After the test method returns, run
lsof | grep $(basename $tempDir)(Linux) orhandle -a | findstr $(basename $tempDir)(Windows) to ensure no processes keep the directory open. - Review Test Report – JUnit outputs a warning if cleanup fails. Look for entries like
Failed to delete temporary directoryin the console orsurefire-reportsXML.
Fixes Tied to Findings
- Close Streams and Join Threads – Ensure all
InputStream/OutputStreamobjects are closed and any spawned threads terminate before the test method ends. - Use Method‑Scoped @TempDir – Replace class‑field injection with a method parameter to guarantee a fresh directory per test.
- Generate Unique Subdirectories – When class‑scoped
@TempDiris required, create subfolders inside the injected path:Path sub = tempDir.resolve("test" + UUID.randomUUID()); Files.createDirectories(sub);. - Avoid Hardcoded Paths – Never reference
/tmporjava.io.tmpdirdirectly; use the injectedPathinstead. - Configure Parallelism – If tests run in parallel, set
junit.jupiter.execution.parallel.enabled=falseinjunit-platform.propertiesor use@Execution(ExecutionMode.SAME_THREAD)on the test class.
Escalation Criteria
If after applying the above fixes the temporary directories still persist, consider:
- Running
mvn test -DtrimStackTrace=falseto capture any hidden stack traces related to cleanup. - Examining OS‑level cleanup policies – some systems may require manual removal of orphaned temp files.
- Reporting the issue to the JUnit team if the framework fails to delete a directory that was created and then closed, indicating a potential bug in the cleanup logic.
Concrete Example
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Files;
import java.nio.file.Path;
class TempDirExample {
@Test
void testCreatesFileInTemp(@TempDir Path tempDir) throws Exception {
// Directory is created before this line runs
assert Files.exists(tempDir);
Path tempFile = tempDir.resolve("sample.txt");
Files.writeString(tempFile, "Hello, world!");
assert Files.exists(tempFile);
}
}
After the test completes, JUnit automatically deletes tempDir and its contents. If you run mvn test twice in a row, you should see no residual directories in the OS temp folder.
Practical Verification
- Run the test suite with
mvn test -DtrimStackTrace=false. - Check the temporary directory path printed by the test (e.g.,
/tmp/junit-12345). - After the suite finishes, execute
ls /tmp | grep junitto ensure nojunit-*entries remain.
Limitations
- Cleanup may fail if the JVM crashes or if the process is killed before the shutdown hook runs.
- On Windows, file locks can prevent deletion until the JVM exits; avoid long‑running background tasks that keep the temp directory open.
- Parallel execution of class‑scoped tests can still collide if they write to the same subfolder; always generate unique subfolder names.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.