2.12. 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](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/extension/ParameterResolver.html)
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 Test Classes and Methods) 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](https://github.com/junit-team/junit5/tree/r5.7.0/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java)
: if a constructor or method parameter is of type[TestInfo](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/TestInfo.html)
, theTestInfoParameterResolver
will supply an instance ofTestInfo
corresponding to the current container or test as the value for the parameter. TheTestInfo
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](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/TestInfo.html)
acts as a drop-in replacement for theTestName
rule from JUnit 4. The following demonstrates how to haveTestInfo
injected into a test 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.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 {
TestInfoDemo(TestInfo testInfo) {
assertEquals("TestInfo Demo", testInfo.getDisplayName());
}
@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() {
}
}
[RepetitionInfoParameterResolver](https://github.com/junit-team/junit5/tree/r5.7.0/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionInfoParameterResolver.java)
: if a method parameter in a@RepeatedTest
,@BeforeEach
, or@AfterEach
method is of type[RepetitionInfo](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/RepetitionInfo.html)
, theRepetitionInfoParameterResolver
will supply an instance ofRepetitionInfo
.RepetitionInfo
can then be used to retrieve information about the current repetition and the total number of repetitions for the corresponding@RepeatedTest
. Note, however, thatRepetitionInfoParameterResolver
is not registered outside the context of a@RepeatedTest
. See Repeated Test Examples.[TestReporterParameterResolver](https://github.com/junit-team/junit5/tree/r5.7.0/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java)
: if a constructor or method parameter is of type[TestReporter](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/TestReporter.html)
, theTestReporterParameterResolver
will supply an instance ofTestReporter
. TheTestReporter
can be used to publish additional data about the current test run. The data can be consumed via thereportingEntryPublished()
method in a[TestExecutionListener](https://junit.org/junit5/docs/current/api/org.junit.platform.launcher/org/junit/platform/launcher/TestExecutionListener.html)
, allowing it to be viewed in IDEs or included in reports.In JUnit Jupiter you should use
TestReporter
where you used to print information tostdout
orstderr
in JUnit 4. Using@RunWith(JUnitPlatform.class)
will output all reported entries tostdout
. In addition, some IDEs print report entries tostdout
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);
}
}
Other parameter resolvers must be explicitly enabled by registering appropriate extensions via @ExtendWith . |
Check out the [RandomParametersExtension](https://github.com/junit-team/junit5-samples/tree/r5.7.0/junit5-jupiter-extensions/src/main/java/com/example/random/RandomParametersExtension.java)
for an example of a custom [ParameterResolver](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/extension/ParameterResolver.html)
. 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](https://github.com/mockito/mockito/blob/release/2.x/subprojects/junit-jupiter/src/main/java/org/mockito/junit/jupiter/MockitoExtension.java)
and the [SpringExtension](https://github.com/spring-projects/spring-framework/tree/HEAD/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java)
.
When the type of the parameter to inject is the only condition for your [ParameterResolver](https://junit.org/junit5/docs/current/api/org.junit.jupiter.api/org/junit/jupiter/api/extension/ParameterResolver.html)
, you can use the generic [TypeBasedParameterResolver](https://github.com/junit-team/junit5/tree/r5.7.0/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolver.java)
base class. The supportsParameters
method is implemented behind the scenes and supports parameterized types.