Spring 引导集成测试通过注释加倍?
Spring Boot Integration test doubles by annotations?
我正在准备一个 Spring 引导程序(用于测试),我希望最终用户可以使用一些测试替身轻松更改生产代码。这些应该是模块化和独立的,即:
- 在一个 IT 中我想模拟 Java 8
Clock @Bean
,我用它来检索系统时间(通过 ZonedDateTime.now(clock)
- 在其他模拟身份验证服务中
- 第三个都做
我目前的解决方案基于 AutoConfiguration
类 with @Profile
and @Primary @Bean
:
@Configuration
@Profile("testClock")
public class FixedClockConfiguration {
@Value("${neostarter.test.clock:2010-01-10T10:00:00Z}")
private String fixedClock;
@Bean
@Primary
Clock clock() {
return Clock.fixed(Instant.parse(fixedClock), TimeZone.getDefault().toZoneId());
}
}
然后要使用它,我需要在我的 IT 中设置 @ActiveProfiles
并提供时钟值,如果我不喜欢默认值 @TestPropertySource
:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
@WebIntegrationTest
@ActiveProfiles("testClock")
@TestPropertySource(properties = "neostarter.test.clock=2015-05-05T10:00:00Z")
public class IntegrationTest {
相同的模式将适用于我 IT 中的所有测试替身,因此我需要添加更多活动配置文件(可能还有一些测试属性):
@ActiveProfiles({"testClock", "testAuth"})
有什么方法可以将其转化为基于注释的解决方案吗?我想实现的是一组简单的注释:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
@WebIntegrationTest
@TestClock("2015-05-05T10:00:00Z")
@TestAuth(roles = {"admin", "user"})
public class IntegrationTest {
会做同样的事情(或者最终结果会是一样的),其中 @TestClock
和 @TestAuth
是完全独立和可选的。
使用 Spring Boot 1.3 并没有特别简单的方法来做到这一点。可能您最好的选择是将注释从测试 class 转移到配置,例如:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration
public class ExampleTests {
@Autowired
private Clock clock;
@Test
public void test() throws Exception {
System.out.println(this.clock);
}
@Configuration
@Import(SampleSimpleApplication.class)
@TestClock("2015-05-05T10:00:00Z")
static class Config {
}
}
然后您可以 @TestClock
导入注册商:
@Retention(RetentionPolicy.RUNTIME)
@Import(TestClockRegistrar.class)
public @interface TestClock {
String value();
}
注册商读取注释并创建 bean:
public class TestClockRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
String pattern = (String) importingClassMetadata
.getAnnotationAttributes(TestClock.class.getName()).get("value");
BeanDefinition beanDefinition = new RootBeanDefinition(Clock.class);
beanDefinition.setFactoryMethodName("fixed");
Instant instant = Instant.parse(pattern);
ZoneId zone = TimeZone.getDefault().toZoneId();
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, instant);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1, zone);
registry.registerBeanDefinition("clock", beanDefinition);
}
}
当然,我不确定这比简单地删除注释要好得多:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration
public class ExampleTests {
@Autowired
private Clock clock;
@Test
public void test() throws Exception {
System.out.println(this.clock);
}
@Configuration
@Import(SampleSimpleApplication.class)
static class Config {
@Bean
@Primary
public Clock clock() {
Clock.fixed(...)
}
}
}
我们正在积极考虑为 Spring Boot 1.4 提供更好的模拟支持。您可以关注 https://github.com/spring-projects/spring-boot/issues/5042 以获取进度。
我正在准备一个 Spring 引导程序(用于测试),我希望最终用户可以使用一些测试替身轻松更改生产代码。这些应该是模块化和独立的,即:
- 在一个 IT 中我想模拟 Java 8
Clock @Bean
,我用它来检索系统时间(通过ZonedDateTime.now(clock)
- 在其他模拟身份验证服务中
- 第三个都做
我目前的解决方案基于 AutoConfiguration
类 with @Profile
and @Primary @Bean
:
@Configuration
@Profile("testClock")
public class FixedClockConfiguration {
@Value("${neostarter.test.clock:2010-01-10T10:00:00Z}")
private String fixedClock;
@Bean
@Primary
Clock clock() {
return Clock.fixed(Instant.parse(fixedClock), TimeZone.getDefault().toZoneId());
}
}
然后要使用它,我需要在我的 IT 中设置 @ActiveProfiles
并提供时钟值,如果我不喜欢默认值 @TestPropertySource
:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
@WebIntegrationTest
@ActiveProfiles("testClock")
@TestPropertySource(properties = "neostarter.test.clock=2015-05-05T10:00:00Z")
public class IntegrationTest {
相同的模式将适用于我 IT 中的所有测试替身,因此我需要添加更多活动配置文件(可能还有一些测试属性):
@ActiveProfiles({"testClock", "testAuth"})
有什么方法可以将其转化为基于注释的解决方案吗?我想实现的是一组简单的注释:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
@WebIntegrationTest
@TestClock("2015-05-05T10:00:00Z")
@TestAuth(roles = {"admin", "user"})
public class IntegrationTest {
会做同样的事情(或者最终结果会是一样的),其中 @TestClock
和 @TestAuth
是完全独立和可选的。
使用 Spring Boot 1.3 并没有特别简单的方法来做到这一点。可能您最好的选择是将注释从测试 class 转移到配置,例如:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration
public class ExampleTests {
@Autowired
private Clock clock;
@Test
public void test() throws Exception {
System.out.println(this.clock);
}
@Configuration
@Import(SampleSimpleApplication.class)
@TestClock("2015-05-05T10:00:00Z")
static class Config {
}
}
然后您可以 @TestClock
导入注册商:
@Retention(RetentionPolicy.RUNTIME)
@Import(TestClockRegistrar.class)
public @interface TestClock {
String value();
}
注册商读取注释并创建 bean:
public class TestClockRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
String pattern = (String) importingClassMetadata
.getAnnotationAttributes(TestClock.class.getName()).get("value");
BeanDefinition beanDefinition = new RootBeanDefinition(Clock.class);
beanDefinition.setFactoryMethodName("fixed");
Instant instant = Instant.parse(pattern);
ZoneId zone = TimeZone.getDefault().toZoneId();
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, instant);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1, zone);
registry.registerBeanDefinition("clock", beanDefinition);
}
}
当然,我不确定这比简单地删除注释要好得多:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration
public class ExampleTests {
@Autowired
private Clock clock;
@Test
public void test() throws Exception {
System.out.println(this.clock);
}
@Configuration
@Import(SampleSimpleApplication.class)
static class Config {
@Bean
@Primary
public Clock clock() {
Clock.fixed(...)
}
}
}
我们正在积极考虑为 Spring Boot 1.4 提供更好的模拟支持。您可以关注 https://github.com/spring-projects/spring-boot/issues/5042 以获取进度。