跳到主要内容

测试夹具的依赖注入

ChatGPT-4o 中英对照 Dependency Injection of Test Fixtures

当你使用 DependencyInjectionTestExecutionListener(默认配置时),测试实例的依赖项会从你使用 @ContextConfiguration 或相关注解配置的应用上下文中的 bean 中注入。你可以使用 setter 注入、字段注入或两者兼用,具体取决于你选择的注解以及你将它们放置在 setter 方法上还是字段上。如果你使用的是 JUnit Jupiter,你还可以选择使用构造函数注入(参见 Dependency Injection with 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 进行字段注入:

@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);
}
}
java

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

@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);
}
}
java

前面的代码列表使用了 @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>
xml
备注

如果你继承自 Spring 提供的测试基类,而该基类恰好在其某个 setter 方法上使用了 @Autowired 注解,你可能在应用程序上下文中定义了多个受影响类型的 bean(例如,多个 DataSource bean)。在这种情况下,你可以重写 setter 方法并使用 @Qualifier 注解来指示特定的目标 bean,如下所示(但请确保也委托给超类中的重写方法):

// ...

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

// ...
java

指定的限定符值指示要注入的特定 DataSource bean,将类型匹配的集合缩小到特定的 bean。其值与相应 <bean> 定义中的 <qualifier> 声明进行匹配。bean 名称用作备用限定符值,因此你也可以通过名称有效地指向特定的 bean(如前所示,假设 myDataSource 是 bean 的 id)。