Enterprise Java

Temporary directories in JUnit 5 Tests

JUnit 4 TemporaryFolder @Rule allowed developers to create tests utilising temporary directories. With JUnit 5, the @Rules are not supported hence testing files and directories required a little bit of additional work. Fortunately, with JUnit 5.4 there is a new built-in extension to handle temporary directories in tests. And it is extremely easy to use.

Are you still working with JUnit 4? See my previous post on testing with files and directories in JUnit 4 with TemporaryFolder @Rule

@TempDir

@org.junit.jupiter.api.io.TempDir annotation can be used in order to annotate class field or a parameter in a lifecycle (e.g. @BeforeEach) or test method of type File or Path. Once this is done, the temporary directory will be be created. The directory with its contents created during test execution will be deleted once the test method or class has finished execution.

The code to be tested

In this simple example, we will test the FileWriter class, that has a single method writing text contents to a new file:

01
02
03
04
05
06
07
08
09
10
public class FileWriter {
 
    public void writeTo(String path, String content) throws IOException {
        Path target = Paths.get(path);
        if (Files.exists(target)) {
            throw new IOException("file already exists");
        }
        Files.copy(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)), target);
    }
}

@TemDir as test method parameter

In this example, we will annotate test parameter with @TempDir annotation:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
import org.junit.jupiter.api.io.TempDir;
 
@Test
void writesContentToFile(@TempDir Path tempDir) throws IOException {
    // arrange
    Path output = tempDir
            .resolve("output.txt");
 
    // act
    fileWriter.writeTo(output.toString(), "test");
 
    // assert
    assertAll(
            () -> assertTrue(Files.exists(output)),
            () -> assertLinesMatch(List.of("test"), Files.readAllLines(output))
    );
}

@TempDir as an instance field

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.junit.jupiter.api.io.TempDir;
 
class FileWriterTest {
 
    private FileWriter fileWriter = new FileWriter();
 
    @TempDir
    Path tempDir;
 
    @BeforeEach
    void beforeEach() {
        assertTrue(Files.isDirectory(this.tempDir));
    }
 
    @RepeatedTest(3)
    void throwsErrorWhenTargetFileExists() throws IOException {
        // arrange
        Path output = Files.createFile(
                tempDir.resolve("output.txt")
        );
 
        // act & assert
        IOException expectedException = assertThrows(IOException.class, () -> fileWriter.writeTo(output.toString(), "test"));
        assertEquals("file already exists", expectedException.getMessage());
    }
}

Based on the above example, we can see that each repetition of the test uses a new temporary directory (according to the standard test class lifecycle) hence the arrange section of the method executes with no error.

Shared temporary directory

In case there is a need to share a temporary directory between test methods, we can create a static field and reuse the temporary directory like in the below example:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.junit.jupiter.api.io.TempDir;
 
class FileWriterTest {
 
    private FileWriter fileWriter = new FileWriter();
 
    @TempDir
    static Path tempDir;
 
    @BeforeAll
    static void setUp() {
        assertTrue(Files.isDirectory(tempDir));
    }
 
    @RepeatedTest(3)
    void throwsErrorWhenTargetFileExists(RepetitionInfo repetitionInfo) throws IOException {
        // arrange
        Path output = Files.createFile(
                tempDir.resolve(repetitionInfo.getCurrentRepetition() + "_output.txt")
        );
 
        // act & assert
        IOException expectedException = assertThrows(IOException.class, () -> fileWriter.writeTo(output.toString(), "test"));
        assertEquals("file already exists", expectedException.getMessage());
    }
}

Please note, that arrange section of the test method creates unique file name per execution (using current repetition counter) as otherwise the FileAlreadyExistsException would have been thrown.

Summary

With @TempDir you get possibility to work with temporary directories in tests with ease. There is no magic here: you annotate Path or File objects and inject as you need them. The rest is taken care by JUnit for you.

Find the examples in my GitHub repository here: https://github.com/kolorobot/junit5-samples/tree/master/junit5-built-in-extensions

Published on Java Code Geeks with permission by Rafal Borowiec, partner at our JCG program. See the original article here: Temporary directories in JUnit 5 Tests

Opinions expressed by Java Code Geeks contributors are their own.

Rafal Borowiec

Software developer, Team Leader, Agile practitioner, occasional blogger, lecturer. Open Source enthusiast, quality oriented and open-minded.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button