In this article we will see Junit 5 Dependency Injection, and what is ParameterResolver and built-in and custom Junit 5 ParameterResolver with examples.
Technologies used in following examples :
- Junit 5.5.2
- Maven 3
- Java 8
- Spring Tool Suite 3.9.8
Project Structure :
1. Junit 5 dependency Injection
1.1. In Junit 5 both test constructors and methods are now permitted to have parameters. This enables Dependency Injection for constructors and methods.
lifecycle per-method : For every test method with in a test class one test instance will be created.
lifecycle per-class : For every test class one test instance will be created, not per test method.
Example :
public class MathUtilTest { // for each test a new instance create (lifecycle per-method default behaviour) public MathUtilTest(TestInfo info) { System.out.println("------------------"); System.out.println("$$$ "+info.getDisplayName()+" $$$"); System.out.println("------------------"); } @BeforeEach void beforeEach(TestInfo testInfo) { // here initialize setup for each test System.out.println("Before Strat test >>>> "+testInfo.getTestMethod().get().getName()); } @Test void test_Add() { System.out.println("test_Add()"); assertEquals(5, MathUtil.add(3, 2)); } @Test void test_Multiply() { System.out.println("test_Multiply()"); assertEquals(15, MathUtil.multiple(3, 5)); } @AfterEach void afterEach(TestInfo testInfo) { // here initialize setup for each test System.out.println("Afetr End test >>>> "+testInfo.getTestMethod().get().getName()); } }
Output :
$$$ MathUtilTest $$$ ------------------ Before Strat test >>>> test_Add test_Add() Afetr End test >>>> test_Add $$$ MathUtilTest $$$ ------------------ Before Strat test >>>> test_Multiply test_Multiply() Afetr End test >>>> test_Multiply
1.2. ParameterResolver
defines the API for Extensions
that wish to dynamically resolve arguments for parameters at runtime. There are currently three built-in resolvers that are registered automatically.
- TestInfoParameterResolver
- RepetitionInfoParameterResolver
- TestReporterParameterResolver
2. TestInfoParameterResolver
- If a constructor or method parameter is of type
TestInfo
, theTestInfoParameterResolver
will supply an instance ofTestInfo
corresponding to the current container or test as the value for the parameter.
public class TestInfoTest { // for each test a new instance create (lifecycle per-method default behaviour) public TestInfoTest(TestInfo info) { System.out.println("------------------"); System.out.println("$$$ "+info.getDisplayName()+" $$$"); System.out.println("------------------"); } @BeforeEach void beforeEach(TestInfo testInfo) { // here initialize setup for each test System.out.println("Before Strat test >>>> "+testInfo.getTestMethod().get().getName()); } @Tag("my-tag") @DisplayName("MathUtil.add_test()") @Test void test_Add(TestInfo testInfo) { System.out.println(">>>> Start test_Add() "); System.out.println(testInfo.getDisplayName()); System.out.println(testInfo.getTestMethod().get().getName()); testInfo.getTags().forEach(System.out::println); assertEquals(5, MathUtil.add(3, 2)); } }
Output :
------------------ $$$ TestInfoTest $$$ ------------------ Before Strat test >>>> test_Add >>>> Start test_Add() MathUtil.add_test() test_Add my-tag
3. RepetitionInfoParameterResolver
If a method parameter in a @RepeatedTest
, @BeforeEach
, or @AfterEach
method is of type RepetitionInfo
, the RepetitionInfoParameterResolver
will supply an instance of RepetitionInfo
.
public class Junit5_RepetitionInfo_Test { @BeforeEach void beforeEach(TestInfo testInfo, RepetitionInfo repetitionInfo) { int currentRepetition = repetitionInfo.getCurrentRepetition(); int totalRepetitions = repetitionInfo.getTotalRepetitions(); String methodName = testInfo.getTestMethod().get().getName(); System.out.println(String.format("About to execute repetition %d of %d for %s", // currentRepetition, totalRepetitions, methodName)); } @RepeatedTest(3) void test_Add(RepetitionInfo repetitionInfo) { System.out.println("start test_Add() : "+repetitionInfo.getCurrentRepetition()); assertEquals(5, MathUtil.add(3, 2)); } @AfterEach void afterEach(TestInfo testInfo, RepetitionInfo repetitionInfo) { int currentRepetition = repetitionInfo.getCurrentRepetition(); int totalRepetitions = repetitionInfo.getTotalRepetitions(); String methodName = testInfo.getTestMethod().get().getName(); System.out.println(String.format("After completed execute repetition %d of %d for %s", // currentRepetition, totalRepetitions, methodName)); } }
Output :
About to execute repetition 1 of 3 for test_Add start test_Add() : 1 After completed execute repetition 1 of 3 for test_Add About to execute repetition 2 of 3 for test_Add start test_Add() : 2 After completed execute repetition 2 of 3 for test_Add About to execute repetition 3 of 3 for test_Add start test_Add() : 3 After completed execute repetition 3 of 3 for test_Add
4. TestReporterParameterResolver
If a constructor or method parameter is of type TestReporter
, the TestReporterParameterResolver
will supply an instance of TestReporter
.
The TestReporter
can be used to publish additional data about the current test run. Some IDEs print report entries to stdout
or display them in the user interface for test results.
public class Junit5_TestReporter_Test { @BeforeEach void beforeEach(TestInfo testInfo, TestReporter testReporter) { String methodName = testInfo.getTestMethod().get().getName(); String reportMessage = String.format("About to execute %s", methodName); testReporter.publishEntry(reportMessage); } @RepeatedTest(3) void test_Add(RepetitionInfo repetitionInfo, TestReporter testReporter) { String key = "start test_Add() : " + repetitionInfo.getCurrentRepetition(); String value = repetitionInfo.getCurrentRepetition() % 2 == 0 ? "FAIL" : "PASS"; assertEquals(5, MathUtil.add(3, 2)); Map<String, String> reportEntryMap = new HashMap<>(); reportEntryMap.put(key, value); testReporter.publishEntry(reportEntryMap); } @AfterEach void afterEach(TestInfo testInfo, TestReporter testReporter) { String methodName = testInfo.getTestMethod().get().getName(); String reportMessage = String.format("Execution completed %s", methodName); testReporter.publishEntry(reportMessage); } }
Output :
TestIdentifier [repetition 1 of 3] ReportEntry [timestamp = 2020-02-21T16:09:05.697836700, value = 'About to execute test_Add'] TestIdentifier [repetition 1 of 3] ReportEntry [timestamp = 2020-02-21T16:09:05.712706700, start test_Add() : 1 = 'PASS'] TestIdentifier [repetition 1 of 3] ReportEntry [timestamp = 2020-02-21T16:09:05.726051200, value = 'Execution completed test_Add'] TestIdentifier [repetition 2 of 3] ReportEntry [timestamp = 2020-02-21T16:09:05.739683, value = 'About to execute test_Add'] TestIdentifier [repetition 2 of 3] ReportEntry [timestamp = 2020-02-21T16:09:05.740727100, start test_Add() : 2 = 'FAIL'] TestIdentifier [repetition 2 of 3] ReportEntry [timestamp = 2020-02-21T16:09:05.742723300, value = 'Execution completed test_Add'] TestIdentifier [repetition 3 of 3] ReportEntry [timestamp = 2020-02-21T16:09:05.746664400, value = 'About to execute test_Add'] TestIdentifier [repetition 3 of 3] ReportEntry [timestamp = 2020-02-21T16:09:05.748692800, start test_Add() : 3 = 'PASS'] TestIdentifier [repetition 3 of 3] ReportEntry [timestamp = 2020-02-21T16:09:05.749802800, value = 'Execution completed test_Add']
5. Create Custom ParameterResolver
Apart from built-in parameter resolvers other parameter resolvers must be explicitly enabled by registering appropriate extensions via @ExtendWith
.
5.1. Create a Custom Parameter Resover by implementing ParameterResolver
interface. Following example demonstrates how to create custom parameter resolver.
FakeDBConnection.java
is a simple java class that you wants to inject to the test method.
public class FakeDBConnection { public List<String> getAllUserNames() { // here your code for - get results from database (just for test, returning dummy values) return Arrays.asList("peterm", "mikek", "johna", "anandv"); } }
FakeDBParameterResolver.java
is a custom parameter resolver that you wants to extend with via @ExtendWith
. ParameterResolver
classes implement two methods :
-
boolean supportsParameter()
– Invoke for all the parameters and returnstrue
if this resolver can resolve an argument for the parameter. If it returnstrue
the parameter will be resolved using the methodresolveParameter()
. Object resolveParameter()
– This method is only called by the framework ifsupportsParameter()
previously returnedtrue
for the sameParameterContext
andExtensionContext
. Returns the resolved argument for the parameter.
public class FakeDBParameterResolver implements ParameterResolver { @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface FakeDB { } @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { return parameterContext.isAnnotated(FakeDB.class); } @Override public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { return getFakeDBConnection(parameterContext.getParameter(), extensionContext); } private Object getFakeDBConnection(Parameter parameter, ExtensionContext extensionContext) { Class<?> type = parameter.getType(); FakeDBConnection connection = extensionContext.getRoot().getStore(Namespace.GLOBAL)// .getOrComputeIfAbsent(FakeDBConnection.class); if (FakeDBConnection.class.equals(type)) return connection; throw new ParameterResolutionException("No FakeDbConnection implemented for " + type); } }
5.2. Test your custom parameter resolver implementation. To use it in your tests you must annotate like
@ExtendWith(FakeDBParameterResolver.class)
@ExtendWith(FakeDBParameterResolver.class) public class CustomParameterResolverTest { @Test void injectFakeDbTest(@FakeDB FakeDBConnection connection) { connection.getAllUserNames().forEach(System.out::println); Assertions.assertTrue(connection.getAllUserNames().contains("peterm")); } }
Output :
peterm mikek johna anandv
You also might be interested in following examples :
- Junit 5 Dynamic Tests
- Junit 5 tags and filter test cases
- Junit 5 Timeout tests
- Junit 5 Repeated tests
- Junit 5 parameterized tests
- Junit 5 parallel test execution
- Junit 5 test order