zoukankan      html  css  js  c++  java
  • ApplicationContextRunner如何简化自动配置测试

     

    1. 概览

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

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

    2. 测试自动化配置方案

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

    private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();
    

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

    2.1. 测试Class Condition

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

    @Configuration
    @ConditionalOnClass(ConditionalOnClassIntegrationTest.class)
    protected static class ConditionalOnClassConfiguration {
        @Bean
        public String created() {
            return "This is created when ConditionalOnClassIntegrationTest is present on the classpath";
        }
    }
     
    @Configuration
    @ConditionalOnMissingClass("com.baeldung.autoconfiguration.ConditionalOnClassIntegrationTest")
    protected static class ConditionalOnMissingClassConfiguration {
        @Bean
        public String missed() {
            return "This is missed when ConditionalOnClassIntegrationTest is present on the classpath";
        }
    }
    

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

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

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

    @Test
    public void whenDependentClassIsPresent_thenBeanCreated() {
        this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
          .run(context -> {
            assertThat(context).hasBean("created");
            assertThat(context.getBean("created"))
              .isEqualTo("This is created when ConditionalOnClassIntegrationTest is present on the classpath");
          });
    }
     
    @Test
    public void whenDependentClassIsPresent_thenBeanMissing() {
        this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
            .run(context -> {
                assertThat(context).doesNotHaveBean("missed");
            });
    }
    

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

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

    @Test
    public void whenDependentClassIsNotPresent_thenBeanMissing() {
        this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
            .withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
            .run((context) -> {
                assertThat(context).doesNotHaveBean("created");
                assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
            });
    }
     
    @Test
    public void whenDependentClassIsNotPresent_thenBeanCreated() {
        this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
          .withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
          .run((context) -> {
            assertThat(context).hasBean("missed");
            assertThat(context).getBean("missed")
              .isEqualTo("This is missed when ConditionalOnClassIntegrationTest is present on the classpath");
            assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
          });
    }
    

    2.2. 测试 Bean Condition

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

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

    @Configuration
    protected static class BasicConfiguration {
        @Bean
        public String created() {
            return "This is always created";
        }
    }
    @Configuration
    @ConditionalOnBean(name = "created")
    protected static class ConditionalOnBeanConfiguration {
        @Bean
        public String createOnBean() {
            return "This is created when bean (name=created) is present";
        }
    }
    @Configuration
    @ConditionalOnMissingBean(name = "created")
    protected static class ConditionalOnMissingBeanConfiguration {
        @Bean
        public String createOnMissingBean() {
            return "This is created when bean (name=created) is missing";
        }
    }
    

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

    @Test
    public void whenDependentBeanIsPresent_thenConditionalBeanCreated() {
        this.contextRunner.withUserConfiguration(BasicConfiguration.class, 
          ConditionalOnBeanConfiguration.class)
        // ommitted for brevity
    }
    @Test
    public void whenDependentBeanIsNotPresent_thenConditionalMissingBeanCreated() {
        this.contextRunner.withUserConfiguration(ConditionalOnMissingBeanConfiguration.class)
        // ommitted for brevity
    }
    

    2.3. 测试 Property Condition

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

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

    com.baeldung.service=custom
    

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

    @Configuration
    @TestPropertySource("classpath:ConditionalOnPropertyTest.properties")
    protected static class SimpleServiceConfiguration {
        @Bean
        @ConditionalOnProperty(name = "com.baeldung.service", havingValue = "default")
        @ConditionalOnMissingBean
        public DefaultService defaultService() {
            return new DefaultService();
        }
        @Bean
        @ConditionalOnProperty(name = "com.baeldung.service", havingValue = "custom")
        @ConditionalOnMissingBean
        public CustomService customService() {
            return new CustomService();
        }
    }
    

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

    @Test
    public void whenGivenCustomPropertyValue_thenCustomServiceCreated() {
        this.contextRunner.withPropertyValues("com.baeldung.service=custom")
            .withUserConfiguration(SimpleServiceConfiguration.class)
            .run(context -> {
                assertThat(context).hasBean("customService");
                SimpleService simpleService = context.getBean(CustomService.class);
                assertThat(simpleService.serve()).isEqualTo("Custom Service");
                assertThat(context).doesNotHaveBean("defaultService");
            });
    }
     
    @Test
    public void whenGivenDefaultPropertyValue_thenDefaultServiceCreated() {
        this.contextRunner.withPropertyValues("com.baeldung.service=default")
            .withUserConfiguration(SimpleServiceConfiguration.class)
            .run(context -> {
                assertThat(context).hasBean("defaultService");
                SimpleService simpleService = context.getBean(DefaultService.class);
                assertThat(simpleService.serve()).isEqualTo("Default Service");
                assertThat(context).doesNotHaveBean("customService");
            });
    }
    

    3. 结论

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

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

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

    本文源代码,请访问GitHub

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

    作者:baeldung

    译者:Leesen

  • 相关阅读:
    自己动手编写一个网络图片爬虫
    使用maven构建项目的注意事项
    maven 构建一个web项目
    构建简单的Maven工程,使用测试驱动的方式开发项目
    像Maven一样构建java项目的目录,更好的管理java工程的源码
    Tomcat源码导入eclipse的步骤
    [HNOI2012] 矿场搭建
    UVA10641 Barisal Stadium 照亮体育馆
    CSP-J/S(NOIP PJ/TG) 游记
    [BalticOI 2011 Day1] Switch the Lamp On
  • 原文地址:https://www.cnblogs.com/liululee/p/11080977.html
Copyright © 2011-2022 走看看