This version is still in development and is not considered stable yet.
For the latest stable version, please use JUnit 6.0.1!

Dependency Injection for Constructors and Methods

In all prior JUnit versions, test constructors or methods were not allowed to have parameters (at least not with the standard Runner implementations). As one of the major changes in JUnit Jupiter, both test constructors and methods are now permitted to have parameters. This allows for greater flexibility and enables Dependency Injection for constructors and methods.

ParameterResolver defines the API for test extensions that wish to dynamically resolve parameters at runtime. If a test class constructor, a test method, or a lifecycle method (see Definitions) accepts a parameter, the parameter must be resolved at runtime by a registered ParameterResolver.

There are currently three built-in resolvers that are registered automatically.

  • TestInfoParameterResolver: 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. The TestInfo can then be used to retrieve information about the current container or test such as the display name, the test class, the test method, and associated tags. The display name is either a technical name, such as the name of the test class or test method, or a custom name configured via @DisplayName.

    TestInfo acts as a drop-in replacement for the TestName rule from JUnit 4. The following demonstrates how to have TestInfo injected into a @BeforeAll method, test class constructor, @BeforeEach method, and @Test method.

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;

@DisplayName("TestInfo Demo")
class TestInfoDemo {

	@BeforeAll
	static void beforeAll(TestInfo testInfo) {
		assertEquals("TestInfo Demo", testInfo.getDisplayName());
	}

	TestInfoDemo(TestInfo testInfo) {
		String displayName = testInfo.getDisplayName();
		assertTrue(displayName.equals("TEST 1") || displayName.equals("test2()"));
	}

	@BeforeEach
	void init(TestInfo testInfo) {
		String displayName = testInfo.getDisplayName();
		assertTrue(displayName.equals("TEST 1") || displayName.equals("test2()"));
	}

	@Test
	@DisplayName("TEST 1")
	@Tag("my-tag")
	void test1(TestInfo testInfo) {
		assertEquals("TEST 1", testInfo.getDisplayName());
		assertTrue(testInfo.getTags().contains("my-tag"));
	}

	@Test
	void test2() {
	}

}
  • RepetitionExtension: if a method parameter in a @RepeatedTest, @BeforeEach, or @AfterEach method is of type RepetitionInfo, the RepetitionExtension will supply an instance of RepetitionInfo. RepetitionInfo can then be used to retrieve information about the current repetition, the total number of repetitions, the number of repetitions that have failed, and the failure threshold for the corresponding @RepeatedTest. Note, however, that RepetitionExtension is not registered outside the context of a @RepeatedTest. See Repeated Test Examples.

  • 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 or attach files to it. The data can be consumed in a TestExecutionListener via the reportingEntryPublished() or fileEntryPublished() method, respectively. This allows them to be viewed in IDEs or included in reports.

    In JUnit Jupiter you should use TestReporter where you used to print information to stdout or stderr in JUnit 4. Some IDEs print report entries to stdout or display them in the user interface for test results.

class TestReporterDemo {

	@Test
	void reportSingleValue(TestReporter testReporter) {
		testReporter.publishEntry("a status message");
	}

	@Test
	void reportKeyValuePair(TestReporter testReporter) {
		testReporter.publishEntry("a key", "a value");
	}

	@Test
	void reportMultipleKeyValuePairs(TestReporter testReporter) {
		Map<String, String> values = new HashMap<>();
		values.put("user name", "dk38");
		values.put("award year", "1974");

		testReporter.publishEntry(values);
	}

	@Test
	void reportFiles(TestReporter testReporter, @TempDir Path tempDir) throws Exception {

		testReporter.publishFile("test1.txt", MediaType.TEXT_PLAIN_UTF_8, file -> Files.write(file, List.of("Test 1")));

		Path existingFile = Files.write(tempDir.resolve("test2.txt"), List.of("Test 2"));
		testReporter.publishFile(existingFile, MediaType.TEXT_PLAIN_UTF_8);

		testReporter.publishDirectory("test3", dir -> {
			Files.write(dir.resolve("nested1.txt"), List.of("Nested content 1"));
			Files.write(dir.resolve("nested2.txt"), List.of("Nested content 2"));
		});

		Path existingDir = Files.createDirectory(tempDir.resolve("test4"));
		Files.write(existingDir.resolve("nested1.txt"), List.of("Nested content 1"));
		Files.write(existingDir.resolve("nested2.txt"), List.of("Nested content 2"));
		testReporter.publishDirectory(existingDir);
	}
}
Other parameter resolvers must be explicitly enabled by registering appropriate extensions via @ExtendWith.

Check out the RandomParametersExtension for an example of a custom ParameterResolver. While not intended to be production-ready, it demonstrates the simplicity and expressiveness of both the extension model and the parameter resolution process. MyRandomParametersTest demonstrates how to inject random values into @Test methods.

@ExtendWith(RandomParametersExtension.class)
class MyRandomParametersTest {

	@Test
	void injectsInteger(@Random int i, @Random int j) {
		assertNotEquals(i, j);
	}

	@Test
	void injectsDouble(@Random double d) {
		assertEquals(0.0, d, 1.0);
	}

}

For real-world use cases, check out the source code for the MockitoExtension and the SpringExtension.

When the type of the parameter to inject is the only condition for your ParameterResolver, you can use the generic TypeBasedParameterResolver base class. The supportsParameters method is implemented behind the scenes and supports parameterized types.