How can I create a junit5
extension/rule/interceptor that automatically wraps each @Test
method.
Example: imagine measuring execution time:
class TimeExension {
runTest() {
StopWatch w = new StopWatch();
w.start();
//actually run the original test
test.run();
w.stop();
}
}
How could this be applied to any test class?
CodePudding user response:
Update: for JUnit 5, you can use @ExtendWith annotation instead of Rules. For example, you can use the TimingExtension showed in the examples of JUnit5.
A full example (partially taken from the previously mentioned example):
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext;
import java.lang.reflect.Method;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.assertEquals;
class TimingExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
private static final Logger logger = Logger.getLogger(TimingExtension.class.getName());
private static final String START_TIME = "start time";
@Override
public void beforeTestExecution(ExtensionContext context) throws Exception {
getStore(context).put(START_TIME, System.currentTimeMillis());
}
@Override
public void afterTestExecution(ExtensionContext context) throws Exception {
Method testMethod = context.getRequiredTestMethod();
long startTime = getStore(context).remove(START_TIME, long.class);
long duration = System.currentTimeMillis() - startTime;
logger.info(() ->
String.format("Method [%s] took %s ms.", testMethod.getName(), duration));
}
private ExtensionContext.Store getStore(ExtensionContext context) {
return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestMethod()));
}
}
@ExtendWith(TimingExtension.class)
class MyFirstJUnitJupiterTests {
@Test
void addition() {
assertEquals(2, 1 1);
}
}
In this case, the output will be something like this:
dec. 21, 2021 12:19:31 DU. TimingExtension afterTestExecution
INFO: Method [addition] took 6 ms.
Process finished with exit code 0
Old answer: In JUnit 4, for that purpose, you can use TestWatchers, Stopwatch with Rules. Here is some examples:
- (1) is for timeout
- (2) do something when the test passes or fails
- (3) calculate execution time
import org.junit.Rule;
import org.junit.rules.TestWatcher;
import org.junit.rules.Timeout;
import org.junit.runner.Description;
class TestBase {
@Rule
public Timeout globalTimeout = Timeout.seconds(3);
@Rule
public TestWatcher watchman = new TestWatcher() {
@Override
protected void failed(Throwable e, Description description) {
TestDescription td = description.getAnnotation(TestDescription.class);
System.out.println("Failed: " td.desc()[0]);
}
@Override
protected void succeeded(Description description) {
}
@Rule
public Stopwatch stopwatch = new Stopwatch() {
@Override
protected void succeeded(long nanos, Description description) {
}
@Override
protected void failed(long nanos, Throwable e, Description description) {
}
};
}
CodePudding user response:
Probably InvocationInterceptor
it the answer:
public class TestDurationReportExtension implements InvocationInterceptor {
@Override
public void interceptTestMethod(Invocation<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext,
ExtensionContext extensionContext) throws Throwable {
long beforeTest = System.currentTimeMillis();
try {
invocation.proceed();
} finally {
long afterTest = System.currentTimeMillis();
long duration = afterTest - beforeTest;
String testClassName = invocationContext.getTargetClass().getSimpleName();
String testMethodName = invocationContext.getExecutable().getName();
System.out.println(String.format("%s.%s: %dms", testClassName, testMethodName, duration));
}
}
}
Use with:
@ExtendWith(TestDurationReportExtension.class)
public class DemoTest { .. }
Idea from: https://www.mscharhag.com/java/junit5-custom-extensions