DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Introduction to Data-Driven Testing With JUnit 5: A Guide to Efficient and Scalable Testing
  • Why Testing is a Long-Term Investment for Software Engineers
  • JUnit, 4, 5, Jupiter, Vintage
  • Readability in the Test: Exploring the JUnitParams

Trending

  • Alternative Structured Concurrency
  • Every Cache Miss Is a Tiny Tax on Your Performance
  • Observability for Agents and Workflows: Tracing Prompts, Tool Calls, and Business Outcomes End-to-End
  • Why Stable RAG Answers Can Still Hide Unstable Evidence
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Lifecycle of JUnit 5's Extension Model

Lifecycle of JUnit 5's Extension Model

JUnit 5 is drawing near, so it's time to look at its new Extension API. Here, we see how to us it and determine exactly when tests are run on your code.

By 
Alex Soto user avatar
Alex Soto
·
Jul. 03, 17 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
13.0K Views

Join the DZone community and get the full member experience.

Join For Free

JUnit 5's final release is around the corner (currently it is M4), and I have started playing with it a bit to see how to write extensions.

In JUnit 5, instead of dealing with Runners, Rules, ClassRules and so on, you've got a single Extension API to implement your own extensions.

JUnit 5 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 here.

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

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 JUnit 5 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>: The first test that it is run is AnotherLoggerExtensionTest. In this case, there is only one simple test, so the lifecycle of extension is BeforeAll, Test Instance-Post-Processing, Before Each, Before Test Execution, then the test itself is executed, and then all After callbacks.

  • <2>: Then the LoggerExtensionTest is executed. The 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>: The second test contains requires a parameter resolution. Parameter resolvers are run after Before events and before executing the test itself.

  • <4>: The last test throws an exception. Test Execution Exception is called after the test is executed but before After events.

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

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

Testing JUnit

Published at DZone with permission of Alex Soto. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Introduction to Data-Driven Testing With JUnit 5: A Guide to Efficient and Scalable Testing
  • Why Testing is a Long-Term Investment for Software Engineers
  • JUnit, 4, 5, Jupiter, Vintage
  • Readability in the Test: Exploring the JUnitParams

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook