Junit 5 Extensions makes the Junit 5 much more powerful and extensible. In this article, we will learn JUnit Extensions.  By using JUnit Extensions we will enhance and extend JUnit capabilities. By extending the behavior of our tests classes and methods, we can use the extended behavior in our test project. In JUnit 4, we can do these operations by using JUnit Rules.

Junit 5 Extensions Overview

In order to use JUnit extensions, we need to register them. When we register extensions, JUnit engine calls them at certain points or in other words extension points during the test execution. We can use below extension points in our test:

  • life-cycle callbacks
  • test instance post-processing
  • conditional test execution
  • exception handling
  • parameter resolution

To use extensions in our projects, we need to create Extension classes and register them to our tests. JUnit extension implements interfaces corresponding to JUnit extension points.

Lifecycle Callbacks and Test Instance Post-Processing Callback

We need to know how and when the extensions of JUnit 5 are invoked in our tests. If we have some uncertainty for this, we may face with unpredicted results. 

Normally, by default JUnit 5 provides us below annotations to manage test life cycle in our tests.

This execution will print below output. This is an expected behavior.

Now. let’s write our test life cycle extension test. First, we need to write our extension class. A sample test life cycle extension class is as follows. We need to implement all callback classes and also TestInstancePostProcessor class.

In order to register our extension class to our test classes, we need to use class level @ExtentWith annotation. You can see this below test class. The code is the same but this time, we registered our extension class with @ExtentWith annotation.

And this time test result will be like that.

As you see above, we can use test lifecycle extensions as Listeners like TestNG Listeners. At each point, you can manipulate your test code with these extensions. Also, BeforeAllCallback and AfterAllCallback are like TestNG‘s @BeforeSuite and @AfterSuite annotations. This is a missing part of the JUnit and now you can use this with JUnit extension models.

Conditional Test Execution

With this extension, we can control the test executions. In order to do this, we need to implement ExecutionCondition interface. We can disable and enable our tests for a specific environment with this extension. For example, let’s set our environment as “development” in our configuration properties file and then implement ExecutionCondition interface in our conditional extension class and write a logic that does not run our tests in the development environment. Finally, we should register this extension with @ExtentWith annotation on top of our test class.

Let’s register this extension.

And this is our properties file.

When you run the test, you will see the results as follows.

Parameter Resolution Extension

In some situations, we need to define parameters for test methods and test constructors. In these kinds of cases, we should use ParameterResolver class to resolve the parameters dynamically at runtime. @Test, @BeforeEach, @AfterEach, @BeforeAll, or @AfterAll method accepts a parameter and this parameter can be resolved by name, annotation, type, etc.

However, if a parameter is defined in JUnit 5 library by default, we will not face any problem. For example, TestInfo is a class defined in JUnit 5 and it comprises information about the current test.

If our test has a parameter as FraudService, it has to be resolved at runtime. Thus, we need to use ParameterResolver extension point.

FraudServiceParameterResolver class will be like that:

In the FraudServiceParameterResolver class, we need to implement  ParameterResolver class. In the supportParameter method, we check the parameter type is as FraudService and the resolveParameter method, we return the new instance of FraudService. In order to use this extension, we should register this with @ExtentWith(FraudServiceParameterResolver.class) annotation on top of our test classes or BaseTest class.

Exception Handling Extension

For specific types of exceptions, we need to implement TestExecutionExceptionHandler interface. The below example demonstrates an ignoring StaleElementReference extension which ignores all StaleElementReferenceException but rethrows other exceptions.

In this article, I tried to explain how to create custom JUnit 5 extensions. You can find sample project for Junit 5 Extensions on swtestacademy GitHub page.

Thanks.
Onur Baskirt