依赖注入
重要性:★★☆☆☆
JUnit Jupiter允许测试类的构造函数、测试方法和生命周期方法接受参数。这些参数在运行时通过预先注册的参数解析器ParameterResolver
的实例进行解析。
1. 内建的参数解析器
有3个内建的参数解析器,在JUnit中它们是自动注册的:
TestInfoParameterResolver
如果测试类的构造函数、测试方法或生命周期方法的参数类型是TestInfo
,内建的参数解析器TestInfoParameterResolver
将提供一个TestInfo
的实例作为这些方法的参数值。可以使用这个TestInfo
实例来检索当前测试容器或测试方法的相关信息,例如显示名称、测试类、测试方法以及相关的标签等。
下面的代码显示如何获取注入构造函数、生命周期函数和测试方法的TestInfo
的内容:
package yang.yu.tdd.di;
import static org.assertj.core.api.Assertions.assertThat;
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) {
assertThat(testInfo.getDisplayName()).isEqualTo("TestInfo Demo");
}
@BeforeEach
void init(TestInfo testInfo) {
String displayName = testInfo.getDisplayName();
assertThat(displayName).isIn("TEST 1", "test2()");
}
@Test
@DisplayName("TEST 1")
@Tag("my-tag")
void test1(TestInfo testInfo) {
assertThat(testInfo.getDisplayName()).isEqualTo("TEST 1");
assertThat(testInfo.getTags()).contains("my-tag");
}
@Test
void test2() {
}
}
RepetitionInfoParameterResolver
如果测试类的构造函数、测试方法或生命周期方法拥有注解@RepeatedTest
、@BeforeEach
、@AfterEach
,并且接受类型为RepetitionInfo
的参数,参数解析器RepetitionInfoParameterResolver
将提供一个RepetitionInfo
实例作为这些方法的参数值。可以从RepetitionInfo
中获取当前重复次数以及总重复次数等相关信息。
下面是示例代码:
package yang.yu.tdd.di;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.RepetitionInfo;
public class RepetitionInfoDemo {
@RepeatedTest(5)
void repeat(RepetitionInfo repetitionInfo) {
System.out.println("Current Repetition: " + repetitionInfo.getCurrentRepetition());
System.out.println("Total Repetitions: " + repetitionInfo.getTotalRepetitions());
}
}
TestReporterParameterResolver
如果测试类的构造函数、测试方法或生命周期方法的参数类型是TestReporter
,参数解析器TestReporterParameterResolver
将提供一个TestReporter
实例作为这些方法的参数值。可以使用这个TestReporter
向当前测试的测试报告添加额外的数据。这些数据可以被TestExecutionListener
的reportingEntryPublished()
方法消费,使它们可以显示在IDE视图和测试报告中。
下面是示例代码:
package yang.yu.tdd.di;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestReporter;
import java.util.HashMap;
import java.util.Map;
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);
}
}
2. 自定义参数解析器
可以通过创建自定义的参数解析器并通过@ExtendWith
注解注册到被测试类,来让测试类构造函数、测试方法、生命周期方法注入特定类型的参数。
下面创建一个自定义的参数解析器RandomParametersExtension
,这个参数解析器在测试方法中查找拥有@Random
注解的int型参数,提供一个随机整数值:
package yang.yu.tdd.di;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Parameter;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
public class RandomParametersExtension implements ParameterResolver {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Random {
}
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.isAnnotated(Random.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return getRandomValue(parameterContext.getParameter(), extensionContext);
}
private Object getRandomValue(Parameter parameter, ExtensionContext extensionContext) {
Class<?> type = parameter.getType();
java.util.Random random = extensionContext.getRoot().getStore(Namespace.GLOBAL)//
.getOrComputeIfAbsent(java.util.Random.class);
if (int.class.equals(type)) {
return random.nextInt();
}
if (double.class.equals(type)) {
return random.nextDouble();
}
throw new ParameterResolutionException("No random generator implemented for " + type);
}
}
下面的代码显示通过注册RandomParametersExtension
参数解析器,给测试方法注入随机整数:
package yang.yu.tdd.di;
import org.assertj.core.data.Offset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import static org.assertj.core.api.Assertions.assertThat;
import static yang.yu.tdd.di.RandomParametersExtension.*;
@ExtendWith(RandomParametersExtension.class)
class MyRandomParametersTest {
@Test
void injectsInteger(@Random int i, @Random int j) {
assertThat(i).isNotEqualTo(j);
}
@Test
void injectsDouble(@Random double d) {
assertThat(d).isCloseTo(0.0, Offset.offset(1.0));
}
}