The TestWatcher interface allows you to monitor the results of test execution. Unlike other callbacks that trigger based on the execution phase, TestWatcher provides specific methods for different outcomes: success, failure, abortion, or disabled tests. It is essentially a 'listener' for the final status of a test.
Java source and doc
Definition of TestWatcherVersion: 6.0.1 package org.junit.jupiter.api.extension;
@API(status = STABLE, since = "5.7")
public interface TestWatcher extends Extension {
default void testDisabled(ExtensionContext context, 1
Optional<String> reason);
default void testSuccessful(ExtensionContext context); 2
default void testAborted(ExtensionContext context, 3
@Nullable Throwable cause);
default void testFailed(ExtensionContext context, 4
@Nullable Throwable cause);
}
Example
In this example, we implement a ResultLoggerExtension. This extension captures the outcome of each test and prints a specific summary. In a real-world scenario, you might use the testFailed method to trigger a database rollback or save a diagnostic log file.
package com.logicbig.example;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestWatcher;
import java.util.Optional;
public class ResultLoggerExtension implements TestWatcher {
@Override
public void testSuccessful(ExtensionContext context) {
System.out.println("✅ SUCCESS: " + context.getDisplayName());
}
@Override
public void testFailed(ExtensionContext context, Throwable cause) {
System.err.println("� FAILED: " + context.getDisplayName() + " | Reason: " + cause.getMessage());
}
@Override
public void testAborted(ExtensionContext context, Throwable cause) {
System.out.println("⚠� ABORTED: " + context.getDisplayName());
}
@Override
public void testDisabled(ExtensionContext context, Optional<String> reason) {
System.out.println("🚫 DISABLED: " + context.getDisplayName() + " | Reason: " + reason.orElse("none"));
}
}
Test Class
package com.logicbig.example;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(ResultLoggerExtension.class)
public class TestWatcherExample {
@Test
void successfulTest() {
Assertions.assertTrue(true);
}
@Test
void failingTest() {
Assertions.fail("Explicit failure to trigger TestWatcher");
}
@Test
@Disabled("Demonstrating disabled callback")
void disabledTest() {
}
}
Output$ mvn test -Dtest=TestWatcherExample [INFO] Scanning for projects... [INFO] [INFO] -------------< com.logicbig.example:junit-5-test-watcher >-------------- [INFO] Building junit-5-test-watcher 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ junit-5-test-watcher --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\junit-5\junit-5-extensions\junit-5-test-watcher\src\main\resources [INFO] [INFO] --- compiler:3.13.0:compile (default-compile) @ junit-5-test-watcher --- [INFO] No sources to compile [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ junit-5-test-watcher --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\junit-5\junit-5-extensions\junit-5-test-watcher\src\test\resources [INFO] [INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ junit-5-test-watcher --- [INFO] Nothing to compile - all classes are up to date. [INFO] [INFO] --- surefire:3.2.5:test (default-test) @ junit-5-test-watcher --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running com.logicbig.example.TestWatcherExample ✅ SUCCESS: successfulTest() 🚫 DISABLED: disabledTest() | Reason: Demonstrating disabled callback [ERROR] Tests run: 3, Failures: 1, Errors: 0, Skipped: 1, Time elapsed: 0.097 s <<< FAILURE! -- in com.logicbig.example.TestWatcherExample [ERROR] com.logicbig.example.TestWatcherExample.failingTest -- Time elapsed: 0.017 s <<< FAILURE! org.opentest4j.AssertionFailedError: Explicit failure to trigger TestWatcher at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:42) at org.junit.jupiter.api.Assertions.fail(Assertions.java:143) at com.logicbig.example.TestWatcherExample.failingTest(TestWatcherExample.java:16)
[INFO] [INFO] Results: [INFO] [ERROR] Failures: [ERROR] TestWatcherExample.failingTest:16 Explicit failure to trigger TestWatcher [INFO] [ERROR] Tests run: 3, Failures: 1, Errors: 0, Skipped: 1 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.364 s [INFO] Finished at: 2026-01-01T08:00:38+08:00 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.2.5:test (default-test) on project junit-5-test-watcher: There are test failures. [ERROR] [ERROR] Please refer to D:\example-projects\junit-5\junit-5-extensions\junit-5-test-watcher\target\surefire-reports for the individual test results. [ERROR] Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream. [ERROR] -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException �?� FAILED: failingTest() | Reason: Explicit failure to trigger TestWatcher
The console output highlights how the extension reacts differently based on the test's success or failure. Notice that testSuccessful was called for the passing test, while testFailed captured the details of the assertion error in the failing test. This capability makes TestWatcher the standard choice for building custom reporting logic within the JUnit 5 ecosystem.
Example ProjectDependencies and Technologies Used: - junit-jupiter-engine 6.0.1 (Module "junit-jupiter-engine" of JUnit)
Version Compatibility: 5.4.0 - 6.0.1 Version compatibilities of junit-jupiter-engine with this example:
- 5.4.0
- 5.4.1
- 5.4.2
- 5.5.0
- 5.5.1
- 5.5.2
- 5.6.0
- 5.6.1
- 5.6.2
- 5.6.3
- 5.7.0
- 5.7.1
- 5.7.2
- 5.8.0
- 5.8.1
- 5.8.2
- 5.9.0
- 5.9.1
- 5.9.2
- 5.9.3
- 5.10.0
- 5.10.1
- 5.10.2
- 5.10.3
- 5.10.4
- 5.10.5
- 5.11.0
- 5.11.1
- 5.11.2
- 5.11.3
- 5.11.4
- 5.12.0
- 5.12.1
- 5.12.2
- 5.13.0
- 5.13.1
- 5.13.2
- 5.13.3
- 5.13.4
- 5.14.0
- 5.14.1
- 6.0.0
- 6.0.1
Versions in green have been tested.
- JDK 25
- Maven 3.9.11
|