JUnit 5 Extension Lifecycle
Update 09.01.2017: The JUnit team decided to include my issue in the next milestone release (M4). With that, the TestInstancePostProcessor extension points will be executed, even if the extension is only active for a single method!
When writing or using extensions in JUnit 5 we should know exactly how and when they are invoked. Not knowing might confuse you with unexpected behavior!
Let’s start with a simple test class containing two tests and all regular lifecycle methods:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class JustTests { @BeforeAll static void beforeAll() {log("beforeAll()");} @BeforeEach void beforeEach() {log("beforeEach()");} @Test void testOne() {log("testOne()");} @Test void testTwo() {log("testTwo()");} @AfterEach void afterEach() {log("afterEach()");} @AfterAll static void afterAll() {log("afterAll()");} } |
Executing this class produces the following output:
1 2 3 4 5 6 7 8 |
beforeAll() beforeEach() testOne() afterEach() beforeEach() testTwo() afterEach() afterAll() |
This is the same basic lifecycle as it has been since JUnit 4 and will serve as our baseline.
Now let’s implement an extension class implementing all available lifecycle extension points:
- TestInstancePostProcessor
- BeforeAllCallback
- BeforeEachCallback
- BeforeTestExecutionCallback
- AfterTestExecutionCallback
- AfterEachCallback
- AfterAllCallback
This extension follows the same principle as the test methods by logging the extension point’s name when executed. When we activate this extension on our test class, we get the following output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
BeforeAllCallback beforeAll() TestInstancePostProcessor BeforeEachCallback beforeEach() BeforeTestExecutionCallback testOne() AfterTestExecutionCallback afterEach() AfterEachCallback TestInstancePostProcessor BeforeEachCallback beforeEach() BeforeTestExecutionCallback testTwo() AfterTestExecutionCallback afterEach() AfterEachCallback afterAll() AfterAllCallback |
This is exactly the behaviour I would expect.
It gets a little bit more interesting when we use our extension on a single test method, instead of the whole class. When we do this, we get the following output:
1 2 3 4 5 6 7 8 9 10 11 12 |
beforeAll() BeforeEachCallback beforeEach() BeforeTestExecutionCallback testOne() AfterTestExecutionCallback afterEach() AfterEachCallback beforeEach() testTwo() afterEach() afterAll() |
As you can see, the static
BeforeAllCallback and
AfterAllCallback as well as the
TestInstancePostProcessor extension points are no longer executed. Since the extension is only used on a single test method, it makes sense that the static callbacks are skipped. In my opinion, skipping the test instance post processing makes a little bit less sense. I have my theories why they skipped it. After all, it is hard to imagine a usecase for processing the test class instance differently for different test methods. But it also would not hurt to execute the post processors, since every method gets its own instance anyway. (See update for the
TestInstancePostProcessor extensions!)
As a final example, let’s clone our extension and change the output to include #1 and #2 as an identification. This will demonstrate how the order, in which extensions are declared, matches the order in which they are invoked.
If we declare the extension like this:
1 2 3 4 5 6 7 8 |
@ExtendWith(AllExtensionPointsOne.class) @ExtendWith(AllExtensionPointsTwo.class) public class MultipleExtensions { @Test void test() {log("test()");} } |
We get output like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
BeforeAllCallback #1 BeforeAllCallback #2 TestInstancePostProcessor #1 TestInstancePostProcessor #2 BeforeEachCallback #1 BeforeEachCallback #2 BeforeTestExecutionCallback #1 BeforeTestExecutionCallback #2 test() AfterTestExecutionCallback #2 AfterTestExecutionCallback #1 AfterEachCallback #2 AfterEachCallback #1 AfterAllCallback #2 AfterAllCallback #1 |
This is no accident – as described in the official documentation (chapter 5.2.1). We use this to our advantage in WebTester’s JUnit 5 support module.
As always, you can find the code of these examples on GitHub. If you want to know more about why the test instance post processor extensions are not invoked on a method level, checkout this issue I opened on GitHub.
Recent posts






Comment article