Core Java

Lifecycle of JUnit 5 Extension Model

JUnit5 final release is around the corner (currently it is M4), and I have started playing with it a bit on how to write extensions.

In JUnit5, instead of dealing with RunnersRulesClassRules and so on, you’ve got a single Extension API to implement your own extensions.

JUnit5 provides several interfaces to hook in its lifecycle. For example you can hook to  Test Instance Post-processing to invoke custom initialization methods on the test instance, or  Parameter Resolution for dynamically resolving test method parameters at runtime. And of course the typical ones like hooking before all tests are executed, before a test is executed, after a test is executed and so on so far, a complete list can be found at  http://junit.org/junit5/docs/current/user-guide/#extensions-lifecycle-callbacks

But in which point of the process it is executed each of them? To test it I have just created an extension that implements all interfaces and each method prints who is it.

public class LoggerExtension implements TestInstancePostProcessor, ParameterResolver, BeforeAllCallback,
    BeforeEachCallback, BeforeTestExecutionCallback, AfterEachCallback, AfterTestExecutionCallback, AfterAllCallback,
    TestExecutionExceptionHandler {
    @Override
    public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
        System.out.println("Test Instance Post-processing called");
    }

    @Override
    public boolean supports(ParameterContext parameterContext, ExtensionContext extensionContext)
        throws ParameterResolutionException {
        System.out.println("Parameter Resolver Supports called");
        return parameterContext.getParameter().getType().equals(String.class);
    }

    @Override
    public Object resolve(ParameterContext parameterContext, ExtensionContext extensionContext)
        throws ParameterResolutionException {
        System.out.println("Resolver called");
        return "Hello World";
    }

    @Override
    public void beforeAll(ContainerExtensionContext context) throws Exception {
        System.out.println("Before All called " + context.getTestClass().get());
    }

    @Override
    public void beforeEach(TestExtensionContext context) throws Exception {
        System.out.println("Before Each called");
    }

    @Override
    public void beforeTestExecution(TestExtensionContext context) throws Exception {
        System.out.println("Before Test Execution called");
    }

    @Override
    public void afterEach(TestExtensionContext context) throws Exception {
        System.out.println("After Each called");
    }

    @Override
    public void afterTestExecution(TestExtensionContext context) throws Exception {
        System.out.println("After Test Executon called");
    }

    @Override
    public void afterAll(ContainerExtensionContext context) throws Exception {
        System.out.println("After All called");
    }

    @Override
    public void handleTestExecutionException(TestExtensionContext context, Throwable throwable) throws Throwable {
        System.out.println("Test Execution Exception called");
        throw throwable;
    }
}

Then I have created a JUnit5 Test suite containing two tests:

@ExtendWith(LoggerExtension.class)
public class AnotherLoggerExtensionTest {

    @Test
    public void test4() {
        System.out.println("Test 4");
    }

}
@ExtendWith(LoggerExtension.class)
public class LoggerExtensionTest {

    @Test
    public void test1() {
        System.out.println("Test 1");
    }

    @Test
    public void test2(String msg) {
        System.out.println("Test 2 " + msg);
    }

    @Test
    public void test3() {
        System.out.println("Test 3");
        throw new IllegalArgumentException("");
    }

}
@RunWith(JUnitPlatform.class)
@SelectClasses({LoggerExtensionTest.class, AnotherLoggerExtensionTest.class})
public class LoggerExtensionTestSuite {
}

So after executing this suite, what it is the output? Let’s see it. Notice that for sake of readability I have added some callouts on terminal output.

Before All called class AnotherLoggerExtensionTest
Test Instance Post-processing called
Before Each called
Before Test Execution called
Test 4
After Test Execution called
After Each called
After All called

// <1>

Before All called class LoggerExtensionTest
Test Instance Post-processing called
Before Each called
Before Test Execution called
Test 1
After Test Execution called
After Each called

// <2>

Test Instance Post-processing called
Before Each called
Before Test Execution called
Parameter Resolver Supports called
Resolver called
Test 2 Hello World
After Test Execution called
After Each called

// <3>

Test Instance Post-processing called
Before Each called
Before Test Execution called
Test 3
Test Execution Exception called
After Test Execution called
After Each called

// <4>

After All called

<1> First test that it is run is AnotherLoggerExtensionTest. In this case there is only one simple test, so the lifecycle of extension is BeforeAllTest Instance-Post-ProcessingBefore EachBefore Test Execution, then the test itself is executed, and then all After callbacks.

<2> Then the LoggerExtensionTest is executed. First test is not a parametrized test, so events related to parameter resolution are not called. Before the test method is executed, test instance post-processing is called, and after that all before events are thrown. Finally the test is executed with all after events.

<3> Second test contains requires a parameter resolution. Parameter resolvers are run after Before events and before executing the test itself.

<4> Last test throws an exception. Test Execution Exception is called after test is executed but before After events.

Last thing to notice is that BeforeAll and AfterAll events are executed per test class and not suite.

The JUnit version used in this example is org.junit.jupiter:junit-jupiter-api:5.0.0-M4

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button