This is a free sample lesson of my Introduction to JUnit 5 course. My JUnit 5 course has 24 lessons, 47 exercises, and 13 quizzes which help you to work smarter and save time when you are writing tests with JUnit 5.
After you have finished this lesson, you:
- Know what a JUnit 5 extension is.
- Can identify the invocation order of extensions, a test class constructor, lifecycle callback methods, and test methods.
- Understand how you can register JUnit 5 extensions.
Let's begin.
What Is a JUnit 5 Extension?
A JUnit 5 extension is a component that can be invoked by JUnit 5 when the execution of a test reaches a specific phase (aka an extension point). When the execution of a test reaches an extension point, JUnit 5 invokes all registered extensions which can be run at that extension point.
If you want to create an extension that can be run at a specific extension point, you have to create an extension class that implements a specific interface provided by the JUnit 5 Extension API. JUnit 5 supports these extension points:
- Test instance pre-construct callback. If you write an extension which is run at this extension point, JUnit 5 runs your extension before it creates a new test instance.
- Test instance factory. You should use this extension point if you have to write an extension that creates new test instances.
- Test instance post-processing. If you write an extension which is run at this extension point, JUnit 5 runs your extension after it has created a new test instance.
- Test lifecycle callbacks. If you use this extension point, you can write an extension that's invoked at different phases of the test execution lifecycle. You can attach your extension to these phases of the test execution lifecycle:
- Your extension is run once before the tests of a test container are run.
- Your extension is run before a test method and the possible setup (
@BeforeEach
) methods are run. - Your extension is run after the possible setup (
@BeforeEach
) methods have been run and before a test method is run. - Your extension is run after a test method have been run and before the possible teardown (
@AfterEach
) methods are run. - Your extension is run after a test method and the possible teardown (
@AfterEach
) methods have been run. - Your extension is run once after all tests of a test container have been run.
- Test instance pre-destroy callback. You should use this extension point if you have to write an extension that's run after your test instances have been used by JUnit 5 and before they are destroyed.
- Conditional test execution. You should use this extension point if you have to write an extension which decides if your test methods should be invoked.
- Parameter resolution. If you write an extension which is run at this extension point, JUnit 5 runs your extension when it has to resolve a parameter of a test class constructor, test method, or lifecycle callback (
@BeforeAll
,@BeforeEach
,@AfterEach
, and@AfterAll
) method at runtime. - Exception handling. You should use this extension point if you have to write an extension which intercepts and handles extensions thrown during the test execution.
- Test result processing. If you write an extension which uses this extension point, JUnit 5 runs your extension when it reports the result of a test method execution.
The following figure demonstrates the invocation order of extensions, a test class constructor, lifecycle callback methods, and test methods when the test class uses the default test instance lifecycle and has a no-argument constructor, all lifecycle callback methods, and one test method (click the image to see the original image):

Next, you will learn how you can register JUnit 5 extensions.
Registering JUnit 5 Extensions
You can register JUnit 5 extensions declaratively, programmatically, or automatically. Let's move on and take a closer look at these extension registration mechanisms.
Registering Extensions Declaratively
If you want to register extensions declaratively, you have to annotate a test interface, test class, or test method with the @ExtendWith
annotation and configure the registered extension. Also, if you are using JUnit Jupiter 5.8 or newer, you can also annotate fields and parameters of test class constructors, test methods, and lifecycle callback (@BeforeAll
, @BeforeEach
, @AfterEach
, and @AfterAll
) methods with the @ExtendWith
annotation.
Let's take a look at some examples which demonstrate how you can register JUnit 5 extensions by using this extension registration mechanism.
Example 1: Register an extension for all tests of a test class
If you want a register an extension for all tests of a test class, you have to annotate the test class with the @ExtendWith
annotation and configure the registered extension. For example, if you want to register an extension called: MyExtension
, the source code of your test class looks as follows:
import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(MyExtension.class) class MyTest { }
Example 2: Register an extension for all tests of a nested test class
If you want to register an extension for all tests of a nested test class, you have to annotate the nested test class with the @ExtendWith
annotation and configure the registered extension. For example, if you want to register an extension called: MyExtension
for all tests of the NestedTestClassA
class, the source code of your test class looks as follows:
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.extension.ExtendWith; class MyTest { @Nested @ExtendWith(MyExtension.class) class NestedTestClassA { } @Nested class NestedTestClassB { } }
Example 3: Register an extension for one test method
If you want to register an extension for one test method, you have to annotate the test method with the @ExtendWith
annotation and configure the registered extension. For example, if you want to register an extension called: MyExtension
, the source code of your test class looks as follows:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; class MyTest { @Test @ExtendWith(MyExtension.class) void test() { } }
Example 4: Register an extension for a field
If you want to register an extension for a field, you have to annotate the field with with the @ExtendWith
annotation and configure the registered extension. For example, if you want to register an extension called: RandomNumberExtension
, the source code of your test class could as follows:
import org.junit.jupiter.api.extension.ExtendWith; class MyTest { @ExtendWith(RandomNumberExtension.class) private static int randomNumberOne; @ExtendWith(RandomNumberExtension.class) private int randomNumberTwo; }
As you might have noticed, the MyTest
class has two fields and one of them is static
. The difference between static
and non-static fields is described in the following:
- If a field is
static
, you can use it everywhere in the test class, including all lifecycle callback methods. - If a field isn't
static
, you can use it everywhere in the test class, except in the@BeforeAll
and@AfterAll
lifecycle callback methods.
Example 5: Register an extension for a parameter
If you want to register an extension for a parameter of a of test class constructor, test method, or lifecycle callback method, you have to annotate the parameter in question with the @ExtendWith
annotation and configure the registered extension. For example, if you want to register an extension called: MyExtension
for a parameter of a test method, your test class looks as follows:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; class MyTest { @Test void test(@ExtendWith(MyExtension.class) String myParameter) { } }
Example 6: Register multiple extensions
If you want to register multiple extensions, you can use one of these two options:
1. You can use one @ExtendWith
annotation and pass the registered extensions as the value of the @ExtendWith
annotation's value
attribute. For example, if you have to register the extensions: MyExtensionOne
and MyExtensionTwo
, the source code of your test class looks as follows:
import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith({MyExtensionOne.class, MyExtensionTwo.class}) class MyTest { }
2. Because the @ExtendWith
annotation is a repetable annotation, You can also use multiple @ExtendWith
annotations. For example, if you have to register the extensions: MyExtensionOne
and MyExtensionTwo
, the source code of your test class looks as follows:
import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(MyExtensionOne.class) @ExtendWith(MyExtensionTwo.class) class MyTest { }
@ExtendWith
annotation, the registered extensions will be run in the order in which they are declared in the source code. For example, the tests found from the MyTest
class will be extended by the MyExtensionOne
and MyExtensionTwo
extensions (in this order).Example 7: Register an extension by using a custom annotation
You can create a custom annotation that registers JUnit 5 extensions. This technique is useful if you want to register multiple extensions in a reusable way or if you have to create an annotation that registers an extension and identifies the target of the registered extension. For example, let's assume that you want to create a custom annotation which can be applied to a field and which registers the RandomNumberExtension
for the target field.
After you have written your custom annotation, its source code looks as follows:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @ExtendWith(RandomNumberExtension.class) public @interface RandomNumber { }
You can now register the RandomNumberExtension
for the fields of your test class by annotating these fields with the @RandomNumber
annotation. After you have done this, the source code of your test class could look as follows:
class MyTest { @RandomNumber private static int randomNumberOne; @RandomNumber private int randomNumberTwo; }
Let's move on and find out how you can register JUnit 5 extensions by using the programmatic extension registration mechanism.
Registering Extensions Programmatically
If you register your extension declaratively by using the @ExtendWith
annotation, typically you have to configure the registered extension by using annotations. If you want to configure the registered extension programmatically (by using the constructor of the registered extension, a static factory method, or a builder), you have to follow these steps:
- Add a new extension field to your test class.
- Create a new extension object and store the created object in the extension field.
- Annotate the extension field with the
@RegisterExtension
annotation.
@ExtendWith
annotation and extensions which are registered programmatically, these extensions will be ordered by using an deterministic algorithm, but order of these extensions isn't obvious. Thus, if you want that your extensions are registered in explicit order, you have annotate the @ExtendWith
and @RegisterExtension
fields with the @Order
annotation.When you register an extension declaratively, you can declare either a static
or non-static extension field. The difference between these two options is described in the following:
1. If the extension field is static
, JUnit 5 will register the extension after the extensions which are registered at class level by using the @ExtendWith
annotation. If you want to register an extension called: MyExtension
by using the programmatic extension registration mechanism, the source code of your test class looks as follows:
import org.junit.jupiter.api.extension.RegisterExtension; class MyTest { @RegisterExtension private static MyExtension extension = MyExtension.getBuilder() .configurationOption("option") .build(); }
2. If the extension field is non-static, the extension will be registered after the test class has been instantiated and the created object has been post-processed (all registered TestInstancePostProcessor
objects have been run). If you want to register an extension called: MyExtension
by using the programmatic extension registration mechanism, the source code of your test class looks as follows:
import org.junit.jupiter.api.extension.RegisterExtension; class MyTest { @RegisterExtension private MyExtension extension = MyExtension.getBuilder() .configurationOption("option") .build(); }
@ExtendWith
annotation, the instance extensions will registered after the extensions which are registered declaratively. However, if you are using the test lifecycle mode: Lifecycle.PER_CLASS
, the instance extensions will be registered before the extensions which are registered declaratively.
Additional Reading:
Next, you will learn how you can use the automatic extension registration mechanism.
Registering Extensions Automatically
The automatic extension registration mechanism uses the Java's ServiceLoader
mechanism and it allows JUnit 5 to auto-detect the extensions found from the classpath and register the found extensions automatically. If you want to leverage the automatic extension registration mechanism, you have to follow these steps:
- Ensure that the jar file which contains the registered extensions has the /META-INF/services directory.
- Add the org.junit.jupiter.api.extension.Extension file to the /META-INF/services directory.
- Add the fully qualified class names of the registered extensions to the org.junit.jupiter.api.extension.Extension file.
- Enable the automatic extension registration mechanism by setting the value of the
junit.jupiter.extensions.autodetection.enabled
configuration option totrue
. You can either put it to the JUnit platform configuration file or pass it to the JVM by using a system property (-Djunit.jupiter.extensions.autodetection.enabled=true).
You know what a JUnit 5 extension is, understand when different extensions are run by JUnit 5, and can register JUnit 5 extensions. Let's summarize what you learned from this lesson.
This is a free sample lesson of my Introduction to JUnit 5 course. My JUnit 5 course has 24 lessons, 47 exercises, and 13 quizzes which help you to work smarter and save time when you are writing tests with JUnit 5.
Summary
This lesson has taught you six things:
- A JUnit 5 extension is a component that can be invoked by JUnit 5 when the execution of a test reaches a specific phase (aka an extension point).
- If you want to register a JUnit 5 extension declaratively, you have to use the
@ExtendWith
annotation. - If you register your extension declaratively, typically you have to configure the registered extension by using annotations. If you register the extension programmatically, you can configure the registered extension by using the constructor of the registered extension, a static factory method, or a builder.
- If you want to register an extension programmatically, you have add a new extension field to your test class, create a new extension object and store the created object in the extension field, and annotate the extension field with the
@RegisterExtension
annotation. - The automatic extension registration mechanism uses the Java’s ServiceLoader mechanism and it allows JUnit 5 to auto-detect the extensions found from the classpath and register the found extensions automatically.
- The automatic extension registration mechanism isn't enabled by default.