美文网首页
【测试相关】如何测试Spring Boot自定义的AutoCon

【测试相关】如何测试Spring Boot自定义的AutoCon

作者: 伊丽莎白2015 | 来源:发表于2022-10-06 16:28 被阅读0次

    1. 创建自己的Auto-configuration

    官网文档(Creating Your Own Auto-configuration)https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration

    更多的Auto-configuration,可以参考spring-cloud-sleuth-autoconfigure项目,它是sleuth的auto-configure模块:
    https://github.com/spring-cloud/spring-cloud-sleuth/tree/3.1.x/spring-cloud-sleuth-autoconfigure

    比如针对@Shcheduled的trace自动装配类:TraceSchedulingAutoConfiguration

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(name = "org.aspectj.lang.ProceedingJoinPoint")
    @ConditionalOnProperty(value = "spring.sleuth.scheduled.enabled", matchIfMissing = true)
    @ConditionalOnBean(Tracer.class)
    @EnableConfigurationProperties(SleuthSchedulingProperties.class)
    @AutoConfigureAfter(BraveAutoConfiguration.class)
    public class TraceSchedulingAutoConfiguration {
        @Bean
        TraceSchedulingAspect traceSchedulingAspect(Tracer tracer, SleuthSchedulingProperties sleuthSchedulingProperties) {
            String skipPatternString = sleuthSchedulingProperties.getSkipPattern();
            Pattern skipPattern = skipPatternString != null ? Pattern.compile(skipPatternString) : null;
            return new TraceSchedulingAspect(tracer, skipPattern);
        }
    }
    

    也可以自已写一个,当项目classpath中有javax.servlet.Filter这个类时,并且com.test.user != false时,就会自动创建bean为userStarterService:

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(Filter.class)
    @ConditionalOnProperty(value = "com.test.user", matchIfMissing = true)
    public class UserAutoConfiguration {
        @Bean
        public UserStarterService userStarterService() {
            return new UserStarterService();
        }
    }
    

    2. 如何通过Unit Test测试我们的Auto-Configure类

    上述我们自己的UserAutoConfiguration类,在现实中可以创建一个空的项目A,再引入这个类所在的starter包,那么运行项目A后会发现没有创建userStarterService bean。或是在项目A中application.yaml,将com.test.user置为false,同样的也不会创建userStarterService bean。

    想要测试创建userStarterService bean的case,那么可以创建创目B,再引入UserAutoConfiguration所在的starter包,将引入spring-boot-starter-web(因为这个包中有Filter类),那么在启动项目B的时候,就会自动创建userStarterService bean。

    上述是后期在集成中的case。但在UserAutoConfiguration所在的starter项目中,应该要有自己的关于这个类的单元测试。即如何测试Spring Boot自定义的AutoConfiguration类?

    官方文档(Testing your Auto-configuration)https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration.testing

    即使用ApplicationContextRunner类进行测试。这个类位于spring-boot-test包中。

    针对主述的UserAutoConfiguration,我们可以写如下的测试类:

    • 首先需要创建ApplicationContextRunner类,并且需要告诉它总是需要加载UserAutoConfiguration.class(这里可以传入多个class)。
    • 测试方法中的Assertions不是JUnit5中的那个类,而是AspectJ中的,(因为JUnit5中的没有assertThat方法)。具体的类为:org.assertj.core.api.Assertions
    • 因为starter本身肯定是需要Filter.class类的(因为在UserAutoConfiguration有用到),那么怎么测试classpath没有Filter.class的情况呢?可以使用ApplicationContextRunner#withClassLoader重写Classpath:
      FilteredClassLoader会将传入它的类在现有的classpath中移除掉。即模拟项目中没有Filter.class类的时候,那么@ConditionalOnClass(Filter.class)不生效,即不会创建userStarterService bean。所以在verify的时候,用的是方法doesNotHaveBean
    • 同样的,ApplicationContextRunner可以模拟property value,例如使用withPropertyValues("com.test.user=false"),可以轻松的测试@ConditionalOnProperty(value = "com.test.user", matchIfMissing = true)不匹配的情况。
    public class UserAutoConfigurationTest {
        private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
                .withConfiguration(AutoConfigurations.of(UserAutoConfiguration.class));
    
        @Test
        public void should_create_bean() {
            contextRunner.run(context -> Assertions.assertThat(context).hasSingleBean(UserStarterService.class));
        }
    
        @Test
        public void should_not_create_bean_as_no_filter_class() {
            contextRunner.withClassLoader(new FilteredClassLoader(Filter.class))
                    .run(context -> Assertions.assertThat(context).doesNotHaveBean(UserStarterService.class));
        }
    
        @Test
        public void should_not_create_bean_as_property_false() {
            contextRunner.withPropertyValues("com.test.user=false")
                    .run(context -> Assertions.assertThat(context).doesNotHaveBean(UserStarterService.class));
        }
    }
    

    3. 更多的sample

    关于#1中引用的Spring Sleuth的TraceSchedulingAutoConfiguration类,它的测试类源码可以看 github

    总体上就是我们上述介绍的方式。sleuth中还有更多的关于Auto-Configure的单元测试,可以在github中看。

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.assertj.core.api.Assertions;
    import org.junit.jupiter.api.Test;
    
    import org.springframework.boot.autoconfigure.AutoConfigurations;
    import org.springframework.boot.test.context.FilteredClassLoader;
    import org.springframework.boot.test.context.runner.ApplicationContextRunner;
    import org.springframework.cloud.sleuth.autoconfig.TraceNoOpAutoConfiguration;
    import org.springframework.cloud.sleuth.instrument.scheduling.TraceSchedulingAspect;
    
    class TraceSchedulingAutoConfigurationTest {
        private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
                .withPropertyValues("spring.sleuth.noop.enabled=true").withConfiguration(
                        AutoConfigurations.of(TraceNoOpAutoConfiguration.class, TraceSchedulingAutoConfiguration.class));
    
        @Test
        void shoud_create_TraceSchedulingAspect() {
            this.contextRunner.run(context -> Assertions.assertThat(context).hasSingleBean(TraceSchedulingAspect.class));
        }
    
        @Test
        void shoud_not_create_TraceSchedulingAspect_without_aspectJ() {
            this.contextRunner.withClassLoader(new FilteredClassLoader(ProceedingJoinPoint.class))
                    .run(context -> Assertions.assertThat(context).doesNotHaveBean(TraceSchedulingAspect.class));
        }
    }
    

    相关文章

      网友评论

          本文标题:【测试相关】如何测试Spring Boot自定义的AutoCon

          本文链接:https://www.haomeiwen.com/subject/wuwmartx.html