构造函数和方法的依赖注入

在之前的所有JUnit版本中,测试构造函数或方法都不允许有参数(至少不能使用标准的Runner实现)。作为JUnit Jupiter的主要变化之一,测试构造函数和方法现在都允许有参数。这带来了更大的灵活性,并为构造函数和方法启用依赖注入。

ParameterResolver定义了测试扩展的API,希望在运行时动态解析参数。如果测试构造函数或@Test@TestFactory@BeforeEach@AfterEach@BeforeAll@AfterAll方法接受参数,则该参数必须在运行时由已注册的ParameterResolver解析。

目前有三个自动注册的内置解析器。

  • TestInfoParameterResolver

    如果方法参数是TestInfo类型,则TestInfoParameterResolver将提供一个TestInfo的实例,对应当前测试,作为参数的值。然后TestInfo可以用来获取有关当前测试的信息,例如测试的显示名称,测试类,测试方法或关联的标签。显示名称可以是技术名称,例如测试类或测试方法的名称,也可以是通过@DisplayName配置的自定义名称。

    TestInfo充当JUnit4的TestName规则的替换品。以下演示如何将TestInfo注入到测试构造函数,@BeforeEach方法和@Test方法中。

    1. import static org.junit.jupiter.api.Assertions.assertEquals;
    2. import static org.junit.jupiter.api.Assertions.assertTrue;
    3. import org.junit.jupiter.api.BeforeEach;
    4. import org.junit.jupiter.api.DisplayName;
    5. import org.junit.jupiter.api.Tag;
    6. import org.junit.jupiter.api.Test;
    7. import org.junit.jupiter.api.TestInfo;
    8. @DisplayName("TestInfo Demo")
    9. class TestInfoDemo {
    10. TestInfoDemo(TestInfo testInfo) {
    11. assertEquals("TestInfo Demo", testInfo.getDisplayName());
    12. }
    13. @BeforeEach
    14. void init(TestInfo testInfo) {
    15. String displayName = testInfo.getDisplayName();
    16. assertTrue(displayName.equals("TEST 1") || displayName.equals("test2()"));
    17. }
    18. @Test
    19. @DisplayName("TEST 1")
    20. @Tag("my-tag")
    21. void test1(TestInfo testInfo) {
    22. assertEquals("TEST 1", testInfo.getDisplayName());
    23. assertTrue(testInfo.getTags().contains("my-tag"));
    24. }
    25. @Test
    26. void test2() {
    27. }
    28. }
  • RepetitionInfoParameterResolver

    如果@RepeatedTest@BeforeEach@AfterEach方法中的方法参数类型为RepetitionInfo,则RepetitionInfoParameterResolver将提供RepetitionInfo的实例。然后可以使用RepetitionInfo获取当前重复信息以及相应的@RepeatedTest的重复总数。但是请注意,RepetitionInfoParameterResolver不在@RepeatedTest的上下文之外注册。请参阅重复测试示例

  • TestReporterParameterResolver

    如果方法参数的类型是TestReporterTestReporterParameterResolver将提供一个TestReporter的实例。TestReporter可用于发布有关当前测试运行的额外数据。数据可以通过TestExecutionListener.reportingEntryPublished()来使用,因此可以被IDE查看或包含在报告中。

    在JUnit Jupiter中,当你需要打印信息时,就像在JUnit4使用stdoutstderr,你应该使用TestReporter。使用@RunWith(JUnitPlatform.class)甚至会将所有报告的条目输出到stdout

    1. import java.util.HashMap;
    2. import org.junit.jupiter.api.Test;
    3. import org.junit.jupiter.api.TestReporter;
    4. class TestReporterDemo {
    5. @Test
    6. void reportSingleValue(TestReporter testReporter) {
    7. testReporter.publishEntry("a key", "a value");
    8. }
    9. @Test
    10. void reportSeveralValues(TestReporter testReporter) {
    11. HashMap<String, String> values = new HashMap<>();
    12. values.put("user name", "dk38");
    13. values.put("award year", "1974");
    14. testReporter.publishEntry(values);
    15. }
    16. }

其他参数解析器必须通过@ExtendWith注册适当的扩展来显式启用。

查看MockitoExtension获取自定义ParameterResolver的示例。虽然不打算生产就绪,它展示了扩展模型和参数解析过程的简单性和表达性。MyMockitoTest演示了如何将Mockito mock注入@BeforeEach@Test方法。

  1. import static org.junit.jupiter.api.Assertions.assertEquals;
  2. import static org.mockito.Mockito.when;
  3. import org.junit.jupiter.api.BeforeEach;
  4. import org.junit.jupiter.api.Test;
  5. import org.junit.jupiter.api.extension.ExtendWith;
  6. import org.mockito.Mock;
  7. import com.example.Person;
  8. import com.example.mockito.MockitoExtension;
  9. @ExtendWith(MockitoExtension.class)
  10. class MyMockitoTest {
  11. @BeforeEach
  12. void init(@Mock Person person) {
  13. when(person.getName()).thenReturn("Dilbert");
  14. }
  15. @Test
  16. void simpleTestWithInjectedMock(@Mock Person person) {
  17. assertEquals("Dilbert", person.getName());
  18. }
  19. }