Java Spring引导集成测试按注释加倍?

Java Spring引导集成测试按注释加倍?,java,spring,spring-boot,spring-test,Java,Spring,Spring Boot,Spring Test,我正在准备一个springbootstarter(用于测试),我想让最终用户可以通过一些测试来轻松地修改生产代码。这些应是模块化和独立的,即: 在其中一个例子中,我想模拟Java8Clock@Bean,我使用它来检索系统时间(通过ZoneDateTime.now(时钟) 在其他模拟身份验证服务中 在第三种情况下,两者都要做 我当前的解决方案基于具有@Profile和@Primary@Bean的自动配置类: @Configuration @Profile("testClock") public

我正在准备一个springbootstarter(用于测试),我想让最终用户可以通过一些测试来轻松地修改生产代码。这些应是模块化和独立的,即:

  • 在其中一个例子中,我想模拟Java8
    Clock@Bean
    ,我使用它来检索系统时间(通过
    ZoneDateTime.now(时钟)
  • 在其他模拟身份验证服务中
  • 在第三种情况下,两者都要做
我当前的解决方案基于具有
@Profile
@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中的所有测试加倍,因此我需要添加更多的活动配置文件(可能还有一些测试属性):

有什么方法可以将其转换为基于注释的解决方案吗?我希望实现的是一组简单的注释:

@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,没有一种特别简单的方法可以做到这一点。您最好的选择可能是将注释从测试类转移到配置,例如:

@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的模拟支持。您可以跟进进度。

感谢您提供的替代方案-我非常喜欢它。不过,我对这个解决方案有两个顾虑,其中一个我发现存在阻碍:我在应用程序中使用@ComponentScan(通过@SpringBootApplication),因此第二个测试类仍然可以看到并使用第一个测试静态类配置(以及所有相关的模拟内容)在TestClockRegistrar之前找不到运行此类的方法。恐怕我对如何在1.3中解决这个问题没有太多想法。希望1.4将提供更多选项供您使用。可能最简单的解决方案是加载不进行组件扫描的不同测试配置。对于静态块,您可以尝试class.forName(“”)来触发加载。谢谢Phil!我成功地克服了这个问题,但使用了一个丑陋的解决方案(将测试内容保留在生产代码中)对于测试配置有单独的元注释,并将其排除在应用程序
@ComponentScan
中。因此,我最终坚持使用概要文件。您能提供更好的模拟支持的任何ETA吗?1.4.0.M2是否仍然代表它?是的,它目前计划用于M2。仅供参考,我用Spring Boot 1.4测试增强完成了我的任务这是我旅程的简短总结:)
@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(...)
        }

    }

}