Temporary directories in JUnit 5 Tests
JUnit 4 TemporaryFolder
@Rule
allowed developers to create tests utilising temporary directories. With JUnit 5, the @Rule
s 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. |