Dependency Injection of Test Fixtures

当你使用 DependencyInjectionTestExecutionListener 时(默认配置),测试实例的依赖关系会从使用 @ContextConfiguration 或相关注释配置的应用程序上下文中注入 bean。你可以使用设置器注入、字段注入,或同时使用这两种方式,这取决于你选择哪些注释以及你是不是将它们放在设置器方法或字段上。如果你使用 JUnit Jupiter,你还可以选择使用构造函数注入(请参阅 Dependency Injection with SpringExtension)。为了与 Spring 的基于注释的注入支持保持一致,你还可以对字段和设置器注入使用 Spring 的 @Autowired 注释或 JSR-330 的 @Inject 注释。

When you use the DependencyInjectionTestExecutionListener (which is configured by default), the dependencies of your test instances are injected from beans in the application context that you configured with @ContextConfiguration or related annotations. You may use setter injection, field injection, or both, depending on which annotations you choose and whether you place them on setter methods or fields. If you are using JUnit Jupiter you may also optionally use constructor injection (see Dependency Injection with SpringExtension). For consistency with Spring’s annotation-based injection support, you may also use Spring’s @Autowired annotation or the @Inject annotation from JSR-330 for field and setter injection.

对于除 JUnit Jupiter 之外的测试框架,TestContext 框架不参与测试类的实例化。因此,在构造函数中使用 @Autowired@Inject 对测试类没有影响。

For testing frameworks other than JUnit Jupiter, the TestContext framework does not participate in instantiation of the test class. Thus, the use of @Autowired or @Inject for constructors has no effect for test classes.

尽管在生产代码中不鼓励字段注入,但字段注入实际上在测试代码中非常自然。这种差异的原理是你将永远不会直接实例化你的测试类。因此,不需要能够在你的测试类上调用 public 构造函数或 setter 方法。

Although field injection is discouraged in production code, field injection is actually quite natural in test code. The rationale for the difference is that you will never instantiate your test class directly. Consequently, there is no need to be able to invoke a public constructor or setter method on your test class.

因为 @Autowired 用于执行 autowiring by type,所以如果您有多个相同类型的 bean 定义,您无法对这些特定 bean 使用此方法。在这种情况下,您可以将 @Autowired@Qualifier 结合使用。您也可以选择将 @Inject@Named 结合使用。或者,如果您的测试类可以访问其 ApplicationContext,您可以通过使用(例如)调用 applicationContext.getBean("titleRepository", TitleRepository.class) 来执行显式查找。

Because @Autowired is used to perform autowiring by type , if you have multiple bean definitions of the same type, you cannot rely on this approach for those particular beans. In that case, you can use @Autowired in conjunction with @Qualifier. You can also choose to use @Inject in conjunction with @Named. Alternatively, if your test class has access to its ApplicationContext, you can perform an explicit lookup by using (for example) a call to applicationContext.getBean("titleRepository", TitleRepository.class).

如果您不希望将依赖注入应用于测试实例,请不要使用 @Autowired@Inject 注释字段或 setter 方法。或者,您可以通过使用 @TestExecutionListeners 显式配置类并从监听器列表中省略 DependencyInjectionTestExecutionListener.class 来完全禁用依赖注入。

If you do not want dependency injection applied to your test instances, do not annotate fields or setter methods with @Autowired or @Inject. Alternatively, you can disable dependency injection altogether by explicitly configuring your class with @TestExecutionListeners and omitting DependencyInjectionTestExecutionListener.class from the list of listeners.

考虑在Goals 部分中概述的测试 HibernateTitleRepository 类的场景。接下来两个代码清单演示了在字段和设置器方法上使用 @Autowired 。应用程序上下文配置在所有示例代码清单之后呈现。

Consider the scenario of testing a HibernateTitleRepository class, as outlined in the Goals section. The next two code listings demonstrate the use of @Autowired on fields and setter methods. The application context configuration is presented after all sample code listings.

以下代码列表中的依赖注入行为不特定于 JUnit Jupiter。相同的 DI 技术可以与任何受支持的测试框架结合使用。

The dependency injection behavior in the following code listings is not specific to JUnit Jupiter. The same DI techniques can be used in conjunction with any supported testing framework.

以下示例调用静态断言方法,例如 assertNotNull(),但不通过 Assertions 作为调用前缀。在这种情况下,假设该方法已通过示例中未显示的 import static 声明正确导入。

The following examples make calls to static assertion methods, such as assertNotNull(), but without prepending the call with Assertions. In such cases, assume that the method was properly imported through an import static declaration that is not shown in the example.

第一个代码列表显示了使用 @Autowired 进行字段注入的 JUnit Jupiter 的基于实现的测试类:

The first code listing shows a JUnit Jupiter based implementation of the test class that uses @Autowired for field injection:

  • Java

  • Kotlin

@ExtendWith(SpringExtension.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {

	// this instance will be dependency injected by type
	@Autowired
	HibernateTitleRepository titleRepository;

	@Test
	void findById() {
		Title title = titleRepository.findById(new Long(10));
		assertNotNull(title);
	}
}
@ExtendWith(SpringExtension::class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {

	// this instance will be dependency injected by type
	@Autowired
	lateinit var titleRepository: HibernateTitleRepository

	@Test
	fun findById() {
		val title = titleRepository.findById(10)
		assertNotNull(title)
	}
}

或者,您可以将类配置为使用 @Autowired 进行 setter 注入,如下所示:

Alternatively, you can configure the class to use @Autowired for setter injection, as follows:

  • Java

  • Kotlin

@ExtendWith(SpringExtension.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {

	// this instance will be dependency injected by type
	HibernateTitleRepository titleRepository;

	@Autowired
	void setTitleRepository(HibernateTitleRepository titleRepository) {
		this.titleRepository = titleRepository;
	}

	@Test
	void findById() {
		Title title = titleRepository.findById(new Long(10));
		assertNotNull(title);
	}
}
@ExtendWith(SpringExtension::class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {

	// this instance will be dependency injected by type
	lateinit var titleRepository: HibernateTitleRepository

	@Autowired
	fun setTitleRepository(titleRepository: HibernateTitleRepository) {
		this.titleRepository = titleRepository
	}

	@Test
	fun findById() {
		val title = titleRepository.findById(10)
		assertNotNull(title)
	}
}

前面的代码列表使用 @ContextConfiguration 注释(即 repository-config.xml)引用的同一个 XML 上下文文件。以下显示此配置:

The preceding code listings use the same XML context file referenced by the @ContextConfiguration annotation (that is, repository-config.xml). The following shows this configuration:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- this bean will be injected into the HibernateTitleRepositoryTests class -->
	<bean id="titleRepository" class="com.foo.repository.hibernate.HibernateTitleRepository">
		<property name="sessionFactory" ref="sessionFactory"/>
	</bean>

	<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
		<!-- configuration elided for brevity -->
	</bean>

</beans>

如果您正在从碰巧在其一个 setter 方法上使用 @Autowired 的 Spring 提供的测试基类扩展,则您可能在应用程序上下文中定义了多个受影响类型的 Bean(例如,多个 DataSource Bean)。在这种情况下,您可以覆盖 setter 方法,并按照如下方式使用 @Qualifier 注释来指示特定的目标 Bean(但请确保也将其委托给超类中的覆盖方法):

If you are extending from a Spring-provided test base class that happens to use @Autowired on one of its setter methods, you might have multiple beans of the affected type defined in your application context (for example, multiple DataSource beans). In such a case, you can override the setter method and use the @Qualifier annotation to indicate a specific target bean, as follows (but make sure to delegate to the overridden method in the superclass as well):

Java
// ...

	@Autowired
	@Override
	public void setDataSource(@Qualifier("myDataSource") DataSource dataSource) {
		super.setDataSource(dataSource);
	}

// ...
Kotlin
// ...

	@Autowired
	override fun setDataSource(@Qualifier("myDataSource") dataSource: DataSource) {
		super.setDataSource(dataSource)
	}

// ...

指定限定符值表示要注入的特定 DataSource Bean,这将类型匹配集缩小到特定 Bean。其值与相应 <bean> 定义中的 <qualifier> 声明匹配。Bean 名称用作备用限定符值,因此您实际上也可以通过名称指向特定 Bean(如先前所示,假定 myDataSource 是 Bean id)。

The specified qualifier value indicates the specific DataSource bean to inject, narrowing the set of type matches to a specific bean. Its value is matched against <qualifier> declarations within the corresponding <bean> definitions. The bean name is used as a fallback qualifier value, so you can effectively also point to a specific bean by name there (as shown earlier, assuming that myDataSource is the bean id).