众所周知,自动配置是Spring Boot的关键功能之一, 但测试自动配置可能会很棘手。

在以下部分中,我们将展示ApplicationContextRunner如何简化自动配置测试。

ApplicationContextRunner是一个实用程序类,它运行ApplicationContext并提供AssertJ样式断言。 最好用作测试类中的字段以便共享配置,然后我们在每个测试中进行自定义:

  1. private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();

让我们通过测试一些案例来展示它的魔力。

在本节中,我们将测试一些使用@ConditionalOnClass和@ConditionalOnMissingClass 注解的自动配置类:

  1. @Configuration
  2. @ConditionalOnClass(ConditionalOnClassIntegrationTest.class)
  3. protected static class ConditionalOnClassConfiguration {
  4. @Bean
  5. public String created() {
  6. return "This is created when ConditionalOnClassIntegrationTest is present on the classpath";
  7. }
  8. }
  9. @Configuration
  10. @ConditionalOnMissingClass("com.baeldung.autoconfiguration.ConditionalOnClassIntegrationTest")
  11. protected static class ConditionalOnMissingClassConfiguration {
  12. @Bean
  13. public String missed() {
  14. return "This is missed when ConditionalOnClassIntegrationTest is present on the classpath";
  15. }
  16. }

我们想测试自动配置是否正确实例化或跳过createdmissing beans给定的预期条件。

  • ApplicationContextRunner为我们提供了withUserConfiguration方法,我们可以根据需要提供自动配置,以便为每个测试自定义ApplicationContext

  • run 方法将 ContextConsumer 作为将断言应用于上下文的参数。 测试退出时,ApplicationContext将自动关闭:

  1. @Test
  2. public void whenDependentClassIsPresent_thenBeanCreated() {
  3.     this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
  4.       .run(context -> {
  5.         assertThat(context).hasBean("created");
  6.         assertThat(context.getBean("created"))
  7.           .isEqualTo("This is created when ConditionalOnClassIntegrationTest is present on the classpath");
  8.       });
  9. }
  10.  
  11. @Test
  12. public void whenDependentClassIsPresent_thenBeanMissing() {
  13.     this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
  14.         .run(context -> {
  15.             assertThat(context).doesNotHaveBean("missed");
  16.         });
  17. }

通过前面的示例,我们发现测试classpath上存在某个类的场景的简单性。但是,当类不在classpath上时,我们如何测试相反的情况呢

这就是FilteredClassLoader发挥作用的地方。它用于在运行时过滤classpath上指定的类:

  1. @Test
  2. public void whenDependentClassIsNotPresent_thenBeanMissing() {
  3.     this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
  4.         .withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
  5.         .run((context) -> {
  6.             assertThat(context).doesNotHaveBean("created");
  7.             assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
  8.         });
  9. }
  10.  
  11. @Test
  12. public void whenDependentClassIsNotPresent_thenBeanCreated() {
  13.     this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
  14.       .withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
  15.       .run((context) -> {
  16.         assertThat(context).hasBean("missed");
  17.         assertThat(context).getBean("missed")
  18.           .isEqualTo("This is missed when ConditionalOnClassIntegrationTest is present on the classpath");
  19.         assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
  20.       });
  21. }

我们刚刚测试了 @ConditionalOnClass 和 @ConditionalOnMissingClass 注解, 现在 让我们看看使用@ConditionalOnBean和@ConditionalOnMissingBean注释时的情况。

首先, 我们同样需要 一些自动配置的类:

  1. @Configuration
  2. protected static class BasicConfiguration {
  3.     @Bean
  4.     public String created() {
  5.         return "This is always created";
  6.     }
  7. }
  8. @Configuration
  9. @ConditionalOnBean(name = "created")
  10. protected static class ConditionalOnBeanConfiguration {
  11.     @Bean
  12.     public String createOnBean() {
  13.         return "This is created when bean (name=created) is present";
  14.     }
  15. }
  16. @Configuration
  17. @ConditionalOnMissingBean(name = "created")
  18. protected static class ConditionalOnMissingBeanConfiguration {
  19.     @Bean
  20.     public String createOnMissingBean() {
  21.         return "This is created when bean (name=created) is missing";
  22.     }
  23. }

然后,我们将像上一节一样调用withUserConfiguration方法,然后发送我们的自定义配置类来测试自动配置是否在不同的条件下恰当地实例化bean或跳过createOnBeancreateOnMissingBean :

  1. @Test
  2. public void whenDependentBeanIsPresent_thenConditionalBeanCreated() {
  3.     this.contextRunner.withUserConfiguration(BasicConfiguration.class,
  4.       ConditionalOnBeanConfiguration.class)
  5.     // ommitted for brevity
  6. }
  7. @Test
  8. public void whenDependentBeanIsNotPresent_thenConditionalMissingBeanCreated() {
  9.     this.contextRunner.withUserConfiguration(ConditionalOnMissingBeanConfiguration.class)
  10.     // ommitted for brevity
  11. }

在本节中,我们测试使用 @ConditionalOnPropertyannotations的自动配置类。

首先,我们需要这个测试的属性:

  1. com.baeldung.service=custom

然后,我们编写嵌套的自动配置类,根据前面的属性创建bean:

  1. @Configuration
  2. @TestPropertySource("classpath:ConditionalOnPropertyTest.properties")
  3. protected static class SimpleServiceConfiguration {
  4.     @Bean
  5.     @ConditionalOnProperty(name = "com.baeldung.service", havingValue = "default")
  6.     @ConditionalOnMissingBean
  7.     public DefaultService defaultService() {
  8.         return new DefaultService();
  9.     }
  10.     @Bean
  11. @ConditionalOnProperty(name = "com.baeldung.service", havingValue = "custom")
  12. @ConditionalOnMissingBean
  13. public CustomService customService() {
  14. return new CustomService();
  15. }
  16. }

现在,我们调用withPropertyValues方法来覆盖每个测试中的属性值:

  1. @Test
  2. public void whenGivenCustomPropertyValue_thenCustomServiceCreated() {
  3. this.contextRunner.withPropertyValues("com.baeldung.service=custom")
  4. .withUserConfiguration(SimpleServiceConfiguration.class)
  5. .run(context -> {
  6. assertThat(context).hasBean("customService");
  7. SimpleService simpleService = context.getBean(CustomService.class);
  8. assertThat(simpleService.serve()).isEqualTo("Custom Service");
  9. assertThat(context).doesNotHaveBean("defaultService");
  10. });
  11. }
  12. @Test
  13. public void whenGivenDefaultPropertyValue_thenDefaultServiceCreated() {
  14. this.contextRunner.withPropertyValues("com.baeldung.service=default")
  15. .withUserConfiguration(SimpleServiceConfiguration.class)
  16. .run(context -> {
  17. assertThat(context).hasBean("defaultService");
  18. SimpleService simpleService = context.getBean(DefaultService.class);
  19. assertThat(simpleService.serve()).isEqualTo("Default Service");
  20. assertThat(context).doesNotHaveBean("customService");
  21. });
  22. }

总结一下, 这篇教程主要展示 如何使用ApplicationContextRunner运行带有自定义的ApplicationContext并应用断言.

我们在这里介绍了最常用的场景,而不是列出如何自定义ApplicationContext 。

在此期间,请记住ApplicationConetxtRunner适用于非Web应用程序,因此请考虑WebApplicationContextRunner用于基于servlet的Web应用程序,ReactiveWebApplicationContextRunner用于响应式Web应用程序。

本文源代码,请访问GitHub

原文:www.baeldung.com/spring-boot…

作者:baeldung

译者:Leesen

 

版权声明:本文为liululee原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/liululee/p/11080977.html