此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.0! |
使用环境配置文件进行上下文配置
Spring 框架对环境和配置文件的概念具有一流的支持
(又名“bean 定义配置文件”),并且可以配置集成测试以激活
用于各种测试场景的特定 bean 定义配置文件。这是通过以下方式实现的
使用@ActiveProfiles
注解并提供
在加载ApplicationContext
进行测试。
您可以使用@ActiveProfiles 与SmartContextLoader SPI 的 API 和@ActiveProfiles 不支持较旧的ContextLoader SPI 的 API 中。 |
考虑两个包含 XML 配置和@Configuration
类:
<!-- app-config.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<bean id="transferService"
class="com.bank.service.internal.DefaultTransferService">
<constructor-arg ref="accountRepository"/>
<constructor-arg ref="feePolicy"/>
</bean>
<bean id="accountRepository"
class="com.bank.repository.internal.JdbcAccountRepository">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="feePolicy"
class="com.bank.service.internal.ZeroFeePolicy"/>
<beans profile="dev">
<jdbc:embedded-database id="dataSource">
<jdbc:script
location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script
location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
<beans profile="default">
<jdbc:embedded-database id="dataSource">
<jdbc:script
location="classpath:com/bank/config/sql/schema.sql"/>
</jdbc:embedded-database>
</beans>
</beans>
-
Java
-
Kotlin
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "classpath:/app-config.xml"
@ContextConfiguration("/app-config.xml")
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// test the transferService
}
}
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from "classpath:/app-config.xml"
@ContextConfiguration("/app-config.xml")
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
lateinit var transferService: TransferService
@Test
fun testTransferService() {
// test the transferService
}
}
什么时候TransferServiceTest
运行,则其ApplicationContext
从app-config.xml
配置文件。如果您检查app-config.xml
中,可以看到accountRepository
Bean 依赖于dataSource
豆。然而dataSource
未定义为顶级 Bean。相反dataSource
定义了三次:在production
配置文件中,在dev
轮廓
在default
轮廓。
通过注释TransferServiceTest
跟@ActiveProfiles("dev")
,我们指示 Spring
TestContext 框架加载ApplicationContext
,并将 Active Profiles 设置为{"dev"}
.因此,将创建一个嵌入式数据库,并使用测试数据填充,并且
这accountRepository
bean 与开发DataSource
.
这可能是我们在集成测试中想要的。
有时,将 bean 分配给default
轮廓。默认值内的 Bean
仅当没有专门激活其他配置文件时,才会包含配置文件。您可以使用
这是为了定义要在应用程序的默认状态中使用的 “fallback” bean。为
例如,您可以显式地为dev
和production
配置 文件
但是,当内存中数据源都未处于活动状态时,请将内存中数据源定义为默认值。
以下代码清单演示了如何实现相同的配置和
集成测试@Configuration
classes 而不是 XML:
-
Java
-
Kotlin
@Configuration
@Profile("dev")
public class StandaloneDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
@Configuration
@Profile("dev")
class StandaloneDataConfig {
@Bean
fun dataSource(): DataSource {
return EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build()
}
}
-
Java
-
Kotlin
@Configuration
@Profile("production")
public class JndiDataConfig {
@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
@Configuration
@Profile("production")
class JndiDataConfig {
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
val ctx = InitialContext()
return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
}
}
-
Java
-
Kotlin
@Configuration
@Profile("default")
public class DefaultDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build();
}
}
@Configuration
@Profile("default")
class DefaultDataConfig {
@Bean
fun dataSource(): DataSource {
return EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build()
}
}
-
Java
-
Kotlin
@Configuration
public class TransferServiceConfig {
@Autowired DataSource dataSource;
@Bean
public TransferService transferService() {
return new DefaultTransferService(accountRepository(), feePolicy());
}
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
@Bean
public FeePolicy feePolicy() {
return new ZeroFeePolicy();
}
}
@Configuration
class TransferServiceConfig {
@Autowired
lateinit var dataSource: DataSource
@Bean
fun transferService(): TransferService {
return DefaultTransferService(accountRepository(), feePolicy())
}
@Bean
fun accountRepository(): AccountRepository {
return JdbcAccountRepository(dataSource)
}
@Bean
fun feePolicy(): FeePolicy {
return ZeroFeePolicy()
}
}
-
Java
-
Kotlin
@SpringJUnitConfig({
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// test the transferService
}
}
@SpringJUnitConfig(
TransferServiceConfig::class,
StandaloneDataConfig::class,
JndiDataConfig::class,
DefaultDataConfig::class)
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
lateinit var transferService: TransferService
@Test
fun testTransferService() {
// test the transferService
}
}
在这个变体中,我们将 XML 配置拆分为四个独立的@Configuration
类:
-
TransferServiceConfig
:获取dataSource
通过使用@Autowired
. -
StandaloneDataConfig
:定义dataSource
对于嵌入式数据库,适用于 开发人员测试。 -
JndiDataConfig
:定义dataSource
从生产中的 JNDI 中检索 环境。 -
DefaultDataConfig
:定义dataSource
对于默认嵌入式数据库,如果为 配置文件处于活动状态。
与基于 XML 的配置示例一样,我们仍然对TransferServiceTest
跟@ActiveProfiles("dev")
,但这次我们通过
使用@ContextConfiguration
注解。测试类本身的主体仍然存在
完全没有改变。
通常情况下,在多个测试类中使用一组配置文件
在给定项目中。因此,为了避免@ActiveProfiles
注解中,你可以声明@ActiveProfiles
once on 基类和 subclasses
自动继承@ActiveProfiles
配置。在
以下示例中,声明@ActiveProfiles
(以及其他注释)
已移动到抽象超类AbstractIntegrationTest
:
测试配置也可以从封闭类继承。看@Nested test 类配置了解详情。 |
-
Java
-
Kotlin
@SpringJUnitConfig({
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
@SpringJUnitConfig(
TransferServiceConfig::class,
StandaloneDataConfig::class,
JndiDataConfig::class,
DefaultDataConfig::class)
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
-
Java
-
Kotlin
// "dev" profile inherited from superclass
class TransferServiceTest extends AbstractIntegrationTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// test the transferService
}
}
// "dev" profile inherited from superclass
class TransferServiceTest : AbstractIntegrationTest() {
@Autowired
lateinit var transferService: TransferService
@Test
fun testTransferService() {
// test the transferService
}
}
@ActiveProfiles
还支持inheritProfiles
属性,可用于
禁用活动配置文件的继承,如下例所示:
-
Java
-
Kotlin
// "dev" profile overridden with "production"
@ActiveProfiles(profiles = "production", inheritProfiles = false)
class ProductionTransferServiceTest extends AbstractIntegrationTest {
// test body
}
// "dev" profile overridden with "production"
@ActiveProfiles("production", inheritProfiles = false)
class ProductionTransferServiceTest : AbstractIntegrationTest() {
// test body
}
此外,有时需要解析测试的活动配置文件 以编程方式而不是以声明方式 — 例如,基于:
-
当前作系统。
-
测试是否在持续集成构建服务器上运行。
-
存在某些环境变量。
-
自定义类级注释的存在。
-
其他问题。
要以编程方式解析活动的 Bean 定义配置文件,您可以实现
自定义ActiveProfilesResolver
并使用resolver
属性@ActiveProfiles
.有关更多信息,请参阅相应的 javadoc。
以下示例演示了如何实现和注册自定义OperatingSystemActiveProfilesResolver
:
-
Java
-
Kotlin
// "dev" profile overridden programmatically via a custom resolver
@ActiveProfiles(
resolver = OperatingSystemActiveProfilesResolver.class,
inheritProfiles = false)
class TransferServiceTest extends AbstractIntegrationTest {
// test body
}
// "dev" profile overridden programmatically via a custom resolver
@ActiveProfiles(
resolver = OperatingSystemActiveProfilesResolver::class,
inheritProfiles = false)
class TransferServiceTest : AbstractIntegrationTest() {
// test body
}
-
Java
-
Kotlin
public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver {
@Override
public String[] resolve(Class<?> testClass) {
String profile = ...;
// determine the value of profile based on the operating system
return new String[] {profile};
}
}
class OperatingSystemActiveProfilesResolver : ActiveProfilesResolver {
override fun resolve(testClass: Class<*>): Array<String> {
val profile: String = ...
// determine the value of profile based on the operating system
return arrayOf(profile)
}
}