Built-in Extensions
While the JUnit team encourages reusable extensions to be packaged and maintained in separate libraries, JUnit Jupiter includes a few user-facing extension implementations that are considered so generally useful that users shouldn’t have to add another dependency.
The @TempDir Extension
The built-in TempDirectory extension is used to create and clean up a temporary
directory for an individual test or all tests in a test class. It is registered by
default. To use it, annotate a non-final, unassigned field of type java.nio.file.Path or
java.io.File with @TempDir or add a parameter of type java.nio.file.Path or
java.io.File annotated with @TempDir to a test class constructor, lifecycle method, or
test method.
For example, the following test declares a parameter annotated with @TempDir for a
single test method, creates and writes to a file in the temporary directory, and checks
its content.
@Test
void writeItemsToFile(@TempDir Path tempDir) throws IOException {
Path file = tempDir.resolve("test.txt");
new ListWriter(file).write("a", "b", "c");
assertEquals(List.of("a,b,c"), Files.readAllLines(file));
}
You can inject multiple temporary directories by specifying multiple annotated parameters.
@Test
void copyFileFromSourceToTarget(@TempDir Path source, @TempDir Path target) throws IOException {
Path sourceFile = source.resolve("test.txt");
new ListWriter(sourceFile).write("a", "b", "c");
Path targetFile = Files.copy(sourceFile, target.resolve("test.txt"));
assertNotEquals(sourceFile, targetFile);
assertEquals(List.of("a,b,c"), Files.readAllLines(targetFile));
}
The following example stores a shared temporary directory in a static field. This
allows the same sharedTempDir to be used in all lifecycle methods and test methods of
the test class. For better isolation, you should use an instance field or constructor
injection so that each test method uses a separate directory.
class SharedTempDirectoryDemo {
@TempDir
static Path sharedTempDir;
@Test
void writeItemsToFile() throws IOException {
Path file = sharedTempDir.resolve("test.txt");
new ListWriter(file).write("a", "b", "c");
assertEquals(List.of("a,b,c"), Files.readAllLines(file));
}
@Test
void anotherTestThatUsesTheSameTempDir() {
// use sharedTempDir
}
}
The @TempDir annotation has an optional cleanup attribute that can be set to either
NEVER, ON_SUCCESS, or ALWAYS. If the cleanup mode is set to NEVER, the temporary
directory will not be deleted after the test completes. If it is set to ON_SUCCESS, the
temporary directory will only be deleted after the test if the test completed successfully.
The default cleanup mode is ALWAYS. You can use the
junit.jupiter.tempdir.cleanup.mode.default
configuration parameter to override this default.
class CleanupModeDemo {
@Test
void fileTest(@TempDir(cleanup = ON_SUCCESS) Path tempDir) {
// perform test
}
}
@TempDir supports the programmatic creation of temporary directories via the optional
factory attribute. This is typically used to gain control over the temporary directory
creation, like defining the parent directory or the file system that should be used.
Factories can be created by implementing TempDirFactory. Implementations must provide a
no-args constructor and should not make any assumptions regarding when and how many times
they are instantiated, but they can assume that their createTempDirectory(…) and
close() methods will both be called once per instance, in this order, and from the same
thread.
The default implementation available in Jupiter delegates directory creation to
java.nio.file.Files::createTempDirectory which uses the default file system and the
system’s temporary directory as the parent directory. It passes junit- as the prefix
string of the generated directory name to help identify it as a created by JUnit.
The following example defines a factory that uses the test name as the directory name
prefix instead of the junit constant value.
class TempDirFactoryDemo {
@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());
}
}
}
It is also possible to use an in-memory file system like Jimfs for the creation of the
temporary directory. The following example demonstrates how to achieve that.
class InMemoryTempDirDemo {
@Test
void test(@TempDir(factory = JimfsTempDirFactory.class) Path tempDir) {
// perform test
}
static class JimfsTempDirFactory implements TempDirFactory {
private final FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix());
@Override
public Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)
throws IOException {
return Files.createTempDirectory(fileSystem.getPath("/"), "junit-");
}
@Override
public void close() throws IOException {
fileSystem.close();
}
}
}
@TempDir can also be used as a meta-annotation to
reduce repetition. The following code listing shows how to create a custom @JimfsTempDir
annotation that can be used as a drop-in replacement for
@TempDir(factory = JimfsTempDirFactory.class).
@TempDir@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@TempDir(factory = JimfsTempDirFactory.class)
@interface JimfsTempDir {
}
The following example demonstrates how to use the custom @JimfsTempDir annotation.
class JimfsTempDirAnnotationDemo {
@Test
void test(@JimfsTempDir Path tempDir) {
// perform test
}
}
Meta-annotations or additional annotations on the field or parameter the TempDir
annotation is declared on might expose additional attributes to configure the factory.
Such annotations and related attributes can be accessed via the AnnotatedElementContext
parameter of the createTempDirectory(…) method.
You can use the junit.jupiter.tempdir.factory.default configuration parameter to specify the fully qualified class name of the
TempDirFactory you would like to use by default. Just like for factories configured via
the factory attribute of the @TempDir annotation, the supplied class has to implement
the TempDirFactory interface. The default factory will be used for all @TempDir
annotations unless the factory attribute of the annotation specifies a different factory.
In summary, the factory for a temporary directory is determined according to the following precedence rules:
-
The
factoryattribute of the@TempDirannotation, if present -
The default
TempDirFactoryconfigured via the configuration parameter, if present -
Otherwise,
org.junit.jupiter.api.io.TempDirFactory$Standardwill be used.
The @AutoClose Extension
The built-in AutoCloseExtension automatically closes resources associated with fields.
It is registered by default. To use it, annotate a field in a test class with
@AutoClose.
@AutoClose fields may be either static or non-static. If the value of an @AutoClose
field is null when it is evaluated the field will be ignored, but a warning message will
be logged to inform you.
By default, @AutoClose expects the value of the annotated field to implement a close()
method that will be invoked to close the resource. However, developers can customize the
name of the close method via the value attribute. For example, @AutoClose("shutdown")
instructs JUnit to look for a shutdown() method to close the resource.
@AutoClose fields are inherited from superclasses. Furthermore, @AutoClose fields from
subclasses will be closed before @AutoClose fields in superclasses.
When multiple @AutoClose fields exist within a given test class, the order in which the
resources are closed depends on an algorithm that is deterministic but intentionally
nonobvious. This ensures that subsequent runs of a test suite close resources in the same
order, thereby allowing for repeatable builds.
The AutoCloseExtension implements the AfterAllCallback and
TestInstancePreDestroyCallback extension APIs. Consequently, a static @AutoClose
field will be closed after all tests in the current test class have completed, effectively
after @AfterAll methods have executed for the test class. A non-static @AutoClose
field will be closed before the current test class instance is destroyed. Specifically, if
the test class is configured with @TestInstance(Lifecycle.PER_METHOD) semantics, a
non-static @AutoClose field will be closed after the execution of each test method, test
factory method, or test template method. However, if the test class is configured with
@TestInstance(Lifecycle.PER_CLASS) semantics, a non-static @AutoClose field will not
be closed until the current test class instance is no longer needed, which means after
@AfterAll methods and after all static @AutoClose fields have been closed.
The following example demonstrates how to annotate an instance field with @AutoClose so
that the resource is automatically closed after test execution. In this example, we assume
that the default @TestInstance(Lifecycle.PER_METHOD) semantics apply.
@AutoClose to close a resourceclass AutoCloseDemo {
@AutoClose (1)
WebClient webClient = new WebClient(); (2)
String serverUrl = // specify server URL ...
@Test
void getProductList() {
// Use WebClient to connect to web server and verify response
assertEquals(200, webClient.get(serverUrl + "/products").getResponseStatus());
}
}
| 1 | Annotate an instance field with @AutoClose. |
| 2 | WebClient implements java.lang.AutoCloseable which defines a close() method that
will be invoked after each @Test method. |