Skip to content

Auto-Registering JUnit 5 extensions

Published on
  • Spring Boot

In rare cases you may want to register certain extension for each test in your test suite. A typical use case that comes to my mind is benchmarking or tracing (look JUnit OpenTelemetry Extension).

A simple but tedious approach is to decorate each test class with @ExtendWith(...). I don't think anyone would have such task especially in a project with large number of test classes. Fortunately, there is a better, less invasive way.

For the sake of example let's create an extension that will measure the time it takes to execute a test:

INFO

For the sake of simplicity I am using StopWatch class from Spring Framework to measure execution time.

java
package com.example.junit;

import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;

import org.springframework.util.StopWatch;

public class BenchmarkExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {

    @Override
    public void beforeTestExecution(ExtensionContext context) {
        var stopWatch = new StopWatch();
        context.getStore(Namespace.create(BenchmarkExtension.class, context.getRequiredTestMethod()))
                .put("stopWatch", stopWatch);
        stopWatch.start();
    }

    @Override
    public void afterTestExecution(ExtensionContext context) {
        var testMethod = context.getRequiredTestMethod();
        var stopWatch = context.getStore(Namespace.create(BenchmarkExtension.class, testMethod))
                .get("stopWatch", StopWatch.class);
        stopWatch.stop();
        context.publishReportEntry(
                String.format("Method %s#%s executed in %d ms", context.getRequiredTestClass().getName(), testMethod.getName(),
                        stopWatch.getLastTaskTimeMillis()));
    }
}

Instead of using @ExtendWith, we will use JUnit Service Loader capabilities to register extension and then enable extension auto-detection:

  1. Create a file src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension with content:
properties
com.example.junit.BenchmarkExtension
  1. Enable extensions auto-detection in src/test/resources/junit-platform.properties:
properties
junit.jupiter.extensions.autodetection.enabled=true

Note, that this enables auto-detection for all extensions registered with service loader in your classpath.

Now, when we run tests, at the end of each test following a statement similar to this one will be printed to the console:

timestamp = 2022-07-26T18:39:56.899219, value = Method com.example.MyService#testSlowMethod executed in 137 ms

While it is good to know that such feature exists, please do not abuse it - it is good to know which extensions are loaded just by looking at the test class.

Let's stay in touch and follow me on Twitter: @maciejwalkowiak

Subscribe to RSS feed