Home Junit 5 Built-in and custom Junit 5 ParameterResolver examples

Built-in and custom Junit 5 ParameterResolver examples

- Advertisement -

In this article we will see Junit 5 Dependency Injection for Constructors and Methods, and what is ParameterResolver and built-in and custom Junit 5 ParameterResolver with examples.

Technologies used in following examples :

  1. Junit 5.5.2
  2. Maven 3
  3. Java 8
  4. Spring Tool Suite 3.9.8

Project Structure :

junit 5 dependency injection and custom parameterresolver

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.

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.

  1. TestInfoParameterResolver
  2. RepetitionInfoParameterResolver
  3. TestReporterParameterResolver

2. TestInfoParameterResolver

  1. If a constructor or method parameter is of type TestInfo, the TestInfoParameterResolver will supply an instance of TestInfo 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() {
		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 :

  1. boolean supportsParameter() – Invoke for all the parameters and returns true if this resolver can resolve an argument for the parameter. If it returns true the parameter will be resolved using the method resolveParameter().
  2. Object resolveParameter() – This method is only called by the framework if supportsParameter() previously returned true for the same ParameterContext and ExtensionContext. 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 :

  1. Junit 5 Dynamic Tests and @TestFactory annotation.
  2. Junit 5 tags and filter test cases for execution.
  3. Junit 5 Timeout tests, fail tests if not completed within time.
  4. Junit 5 Repeated tests and display Repetition info.
  5. Junit 5 parameterized tests with different argument sources.
  6. Executing Junit Jupiter tests parallel and Synchronization access for shared resources.
Satish Varma
Satish Varmahttps://javabydeveloper.com
Satish is post graduated in master of computer applications and experienced software engineer with focus on Spring, JPA, REST, TDD and web development. Also founder of javabydeveloper.com. Follow him on LinkedIn or Twitter or Facebook

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Stay in Touch

Categories