测试夹具的依赖注入

当您使用DependencyInjectionTestExecutionListener(默认配置)时,您的测试实例的依赖项将从您使用@ContextConfiguration或相关注解配置的应用程序上下文的 Bean 中注入。您可以使用 setter 注入、字段注入或两者兼用,具体取决于您选择的注解以及您是在 setter 方法还是字段上放置它们。如果您使用的是 JUnit Jupiter,您还可以选择使用构造函数注入(请参阅使用SpringExtension进行依赖注入)。为了与 Spring 的基于注解的注入支持保持一致,您还可以使用 Spring 的@Autowired注解或 JSR-330 的@Inject注解进行字段和 setter 注入。

对于除 JUnit Jupiter 之外的其他测试框架,TestContext 框架不参与测试类的实例化。因此,对于测试类,使用构造函数的@Autowired@Inject无效。
尽管在生产代码中不鼓励使用字段注入,但在测试代码中字段注入实际上非常自然。差异的基本原理是您永远不会直接实例化您的测试类。因此,无需能够在您的测试类上调用public构造函数或 setter 方法。

因为@Autowired用于执行按类型自动装配,如果您有多个相同类型的 Bean 定义,则不能依赖此方法来处理这些特定的 Bean。在这种情况下,您可以将@Autowired@Qualifier结合使用。您还可以选择将@Inject@Named结合使用。或者,如果您的测试类可以访问其ApplicationContext,则可以使用(例如)对applicationContext.getBean("titleRepository", TitleRepository.class)的调用来执行显式查找。

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

考虑测试HibernateTitleRepository类的场景,如目标部分所述。接下来的两个代码清单演示了在字段和 setter 方法上使用@Autowired

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

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

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

  • 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 注入,如下所示

  • 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注解引用的相同的 XML 上下文文件(即repository-config.xml)。以下是此配置

<?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>

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

  • Java

  • Kotlin

// ...

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

// ...
// ...

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

// ...

指定的限定符值指示要注入的特定DataSource Bean,将类型匹配集缩小到特定的 Bean。其值与相应<bean>定义内的<qualifier>声明匹配。Bean 名称用作后备限定符值,因此您可以有效地在那里也指向特定的 Bean(如前所述,假设myDataSource是 Bean 的id)。