Dependency Injection and Testing in JUnit 5
1. Dependency injection in JUnit 5
In all prior JUnit versions, test constructors or methods weren’t allowed to have parameters. As one of the major changes in JUnit 5, 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 accepts a parameter, the parameter must be resolved at runtime by a registered ParameterResolver. You can inject as many parameters as you want in any order you want them to be.
Although there are currently three built-in resolvers, other parameter resolvers must be explicitly enabled by registering appropriate extensions via @ExtendWith. The automatically registered parameter resolvers are:
- TestInfoParameterResolver: if a constructor or method parameter is of type TestInfo, the TestInfoParameterResolver supplies an instance of TestInfo corresponding to the current container or test as the value for the parameter. TestInfo is a class whose objects are used to inject information about the currently executed test or container into to @Test, @BeforeEach, @AfterEach, @BeforeAll, and @AfterAll methods. 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. Listing 1 shows how to use a TestInfo parameter as argument of a constructor and of annotated methods.
Into the previous example, a TestInfo parameter is injected into the constructor and into three methods. The constructor verifies that the display name is exactly TestInfoTest, its own name (1). This is the default behavior which you may vary with the help of the @DisplayName annotations, as you’ll see immediately.
Then, the @BeforeEach annotated method is executed before each test. It has an injected TestInfo parameter and it verifies that the displayed name is the expected one, meaning either the name of the method or the one specified by the @DisplayName annotation (2).
Both tests have an injected TestInfo parameter. Each of them verifies that the displayed name is the expected one, meaning either the name of the method for the first test (3) or the one specified by the @DisplayName annotation for the second test (4).
We remind that the built-in TestInfoParameterResolver supplies an instance of TestInfo corresponding to the current container or test as the value for the expected parameters of the constructor and of the methods.
- TestReporterParameterResolver: if a constructor or method parameter is of type TestReporter, the TestReporterParameterResolver supplies an instance of TestReporter. TestReporter is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference. Parameters of type TestReporter can be injected into methods of test classes annotated with @BeforeEach, @AfterEach, and @Test. The TestReporter can be used to publish additional data about the current test run. Listing 2 shows how to use use a TestReporter parameter as argument of @Test annotated methods.
Into the previous example, a TestReporter parameter is injected into three methods.
Into the first method, it’s used for publishing a single value entry (1).
Into the second method, it’s used for publishing a key-value pair (2).
Into the third method, we first construct a map (3), then populate it with two key-values pairs (4), then we use it for publishing the constructed map (5).
We remind that the built-in TestReporterParameterResolver supplies the instance of TestReporter needed for publishing the entries.
The result of the execution of this test is shown below.
- RepetitionInfoParameterResolver: if a method parameter in a @RepeatedTest, @BeforeEach, or @AfterEach method is of type RepetitionInfo, the RepetitionInfoParameterResolver supplies an instance of RepetitionInfo. RepetitionInfo can then be used to retrieve information about the current repetition and the total number of repetitions for the corresponding @RepeatedTest. RepetitionInfoParameterResolver isn’t registered outside the context of a @RepeatedTest. We’ll discuss more about repeated tests and provide examples into the next section.
Interested in Java? Check out our trainings.
Java and Web Technologies Expert