为什么构造函数注入中@SpringBootTest需要@Autowired

nyk*_*kon 7 testing junit spring mockito kotlin

一个更普遍的问题。如果在常规 Spring 托管类中使用构造函数注入,则类将自动装配,而不需要 @Autowired 注释,即:

\n
@Service\nclass MailService(\n  private val projectService: ProjectService,\n  private val mailer: Mailer\n) { ...\xc2\xa0}\n
Run Code Online (Sandbox Code Playgroud)\n

在 @SpringBootTest 类中遵循相同的构造函数注入原则,您需要将 @Autowired 注解设置为构造函数参数,否则将无法注入该类,即:

\n
@SpringBootTest\ninternal class MailerTest(\n  @Autowired private val mailer: Mailer\n) { ... }\n
Run Code Online (Sandbox Code Playgroud)\n

为什么会出现这种差异呢?

\n

Les*_*iak 11

对于 SpringBoot 应用程序,是 spring 负责连接 bean。

对于 JUnit 5,Spring 管理的 Bean 必须注入到 JUnit 管理的测试类的实例中。幸运的是,JUnit 5 提供了一种通过ParameterResolver来做到这一点的方法。

@SpringBootTest注册 SpringExtension,它除了其他功能外还用作 ParameterResolver:

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
    Parameter parameter = parameterContext.getParameter();
    Executable executable = parameter.getDeclaringExecutable();
    Class<?> testClass = extensionContext.getRequiredTestClass();
    PropertyProvider junitPropertyProvider = propertyName ->
    extensionContext.getConfigurationParameter(propertyName).orElse(null);
    return (TestConstructorUtils.isAutowirableConstructor(executable, testClass, junitPropertyProvider) ||
            ApplicationContext.class.isAssignableFrom(parameter.getType()) ||
            supportsApplicationEvents(parameterContext) ||
            ParameterResolutionDelegate.isAutowirable(parameter, parameterContext.getIndex()));
}
Run Code Online (Sandbox Code Playgroud)

ParameterResolutionDelegate.isAutowirable依赖注解来判断是否可以从Spring的ApplicationContext注入参数

public static boolean isAutowirable(Parameter parameter, int parameterIndex) {
    Assert.notNull(parameter, "Parameter must not be null");
    AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter(parameter, parameterIndex);
    return (AnnotatedElementUtils.hasAnnotation(annotatedParameter, Autowired.class) ||
            AnnotatedElementUtils.hasAnnotation(annotatedParameter, Qualifier.class) ||
            AnnotatedElementUtils.hasAnnotation(annotatedParameter, Value.class));
}
Run Code Online (Sandbox Code Playgroud)

事实上,如果省略 @Autowired 注解,JUnit 会抱怨缺少 ParameterResolver:

org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [test.Mailer mailer] in constructor [public test.MailServiceTest(test.Mailer)].
Run Code Online (Sandbox Code Playgroud)