On this page
JUnit 5 Extensions
JUnit 5’s extension model replaces JUnit 4’s rules and runners. Extensions hook into the test lifecycle to add custom behavior.
Built-in Extensions
@ExtendWith — Register Extensions
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock UserRepository repository;
@InjectMocks UserService userService;
}
TempDir — Temporary Directories
@Test
void shouldWriteFile(@TempDir Path tempDir) throws IOException {
Path file = tempDir.resolve("output.txt");
Files.writeString(file, "Hello");
assertEquals("Hello", Files.readString(file));
}
// tempDir is automatically cleaned up after the test
RepeatedTest
@RepeatedTest(5)
void shouldBeReliable() {
assertTrue(service.isAvailable());
}
@RepeatedTest(value = 3, name = "{displayName} - repetition {currentRepetition}/{totalRepetitions}")
void repeatedWithName(RepetitionInfo info) {
System.out.println("Run " + info.getCurrentRepetition());
}
Conditional Execution
@Test
@EnabledOnOs(OS.LINUX)
void linuxOnlyTest() { }
@Test
@DisabledOnOs(OS.WINDOWS)
void notOnWindows() { }
@Test
@EnabledIfSystemProperty(named = "env", matches = "ci")
void ciOnlyTest() { }
@Test
@EnabledIfEnvironmentVariable(named = "RUN_INTEGRATION", matches = "true")
void integrationTest() { }
Custom condition:
@Test
@EnabledIf("customCondition")
void conditionalTest() {
boolean customCondition() {
return Database.isAvailable();
}
}
Writing Custom Extensions
public class TimingExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
private long startTime;
@Override
public void beforeTestExecution(ExtensionContext context) {
startTime = System.currentTimeMillis();
}
@Override
public void afterTestExecution(ExtensionContext context) {
long duration = System.currentTimeMillis() - startTime;
System.out.println(context.getDisplayName() + " took " + duration + "ms");
}
}
Usage:
@ExtendWith(TimingExtension.class)
class TimedTests {
@Test
void slowTest() throws InterruptedException {
Thread.sleep(100);
}
}
Global Extension Registration
Apply to all tests via META-INF/services:
src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension:
com.example.TimingExtension
Or in junit-platform.properties:
junit.jupiter.extensions.autodetection.enabled=true
Callback Order
BeforeAll → BeforeEach → BeforeTestExecution → Test → AfterTestExecution → AfterEach → AfterAll
Extensions can implement multiple callback interfaces in one class.
Best Practices
- Use built-in extensions (
TempDir,MockitoExtension) before writing custom ones - Register global extensions sparingly — prefer explicit
@ExtendWith - Use conditional annotations to skip tests in unsupported environments
- Custom extensions should be stateless or use
ExtensionContext.Storefor state - Prefer extensions over JUnit 4
@Rulepatterns