Spring、Spring Boot和TestNG测试指南 - 测试@Configuration


声明:本文转载自https://my.oschina.net/sqq/blog/1529030,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

码云地址

在Spring引入Java Config机制之后,我们会越来越多的使用@Configuration来注册Bean,并且Spring Boot更广泛地使用了这一机制,其提供的大量Auto Configuration大大简化了配置工作。那么问题来了,如何确保@Configuration和Auto Configuration按照预期运行呢,是否正确地注册了Bean呢?本章举例测试@Configuration和Auto Configuration的方法(因为Auto Configuration也是@Configuration,所以测试方法是一样的)。

例子1:测试@Configuration

我们先写一个简单的@Configuration:

@Configuration public class FooConfiguration {    @Bean   public Foo foo() {     return new Foo();   }  } 

然后看FooConfiguration是否能够正确地注册Bean:

public class FooConfigurationTest {    private AnnotationConfigApplicationContext context;    @BeforeMethod   public void init() {     context = new AnnotationConfigApplicationContext();   }    @AfterMethod(alwaysRun = true)   public void reset() {     context.close();   }    @Test   public void testFooCreation() {     context.register(FooConfiguration.class);     context.refresh();     assertNotNull(context.getBean(Foo.class));   }  } 

注意上面代码中关于Context的代码:

  1. 首先,我们构造一个Context
  2. 然后,注册FooConfiguration
  3. 然后,refresh Context
  4. 最后,在测试方法结尾close Context

如果你看Spring Boot中关于@Configuration测试的源代码会发现和上面的代码有点不一样:

public class DataSourceAutoConfigurationTests {  	private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();  	@Before 	public void init() { 		EmbeddedDatabaseConnection.override = null; 		EnvironmentTestUtils.addEnvironment(this.context, 				"spring.datasource.initialize:false", 				"spring.datasource.url:jdbc:hsqldb:mem:testdb-" + new Random().nextInt()); 	}  	@After 	public void restore() { 		EmbeddedDatabaseConnection.override = null; 		this.context.close(); 	} 

这是因为Spring和Spring Boot都是用JUnit做测试的,而JUnit的特性是每次执行测试方法前,都会new一个测试类实例,而TestNG是在共享同一个测试类实例的。

例子2:测试@Conditional

Spring Framework提供了一种可以条件控制@Configuration的机制,即只在满足某条件的情况下才会导入@Configuration,这就是@Conditional

下面我们来对@Conditional做一些测试,首先我们自定义一个Condition FooConfiguration

@Configuration public class FooConfiguration {    @Bean   @Conditional(FooCondition.class)   public Foo foo() {     return new Foo();   }    public static class FooCondition implements Condition {      @Override     public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {       if (context.getEnvironment() != null) {         Boolean property = context.getEnvironment().getProperty("foo.create", Boolean.class);         return Boolean.TRUE.equals(property);       }       return false;     }    } } 

该Condition判断Environment中是否有foo.create=true

如果我们要测试这个Condition,那么就必须往Environment里添加相关property才可以,在这里我们测试了三种情况:

  1. 没有配置foo.create=true
  2. 配置foo.create=true
  3. 配置foo.create=false

FooConfigurationTest

public class FooConfigurationTest {    private AnnotationConfigApplicationContext context;    @BeforeMethod   public void init() {     context = new AnnotationConfigApplicationContext();   }    @AfterMethod(alwaysRun = true)   public void reset() {     context.close();   }    @Test(expectedExceptions = NoSuchBeanDefinitionException.class)   public void testFooCreatePropertyNull() {     context.register(FooConfiguration.class);     context.refresh();     context.getBean(Foo.class);   }    @Test   public void testFooCreatePropertyTrue() {     context.getEnvironment().getPropertySources().addLast(         new MapPropertySource("test", Collections.singletonMap("foo.create", "true"))     );     context.register(FooConfiguration.class);     context.refresh();     assertNotNull(context.getBean(Foo.class));   }    @Test(expectedExceptions = NoSuchBeanDefinitionException.class)   public void testFooCreatePropertyFalse() {     context.getEnvironment().getPropertySources().addLast(         new MapPropertySource("test", Collections.singletonMap("foo.create", "false"))     );     context.register(FooConfiguration.class);     context.refresh();     assertNotNull(context.getBean(Foo.class));   }  } 

注意我们用以下方法来给Environment添加property:

context.getEnvironment().getPropertySources().addLast(   new MapPropertySource("test", Collections.singletonMap("foo.create", "true")) ); 

所以针对@Conditional和其对应的Condition的测试的根本就是给它不一样的条件,判断其行为是否正确,在这个例子里我们的Condition比较简单,只是判断是否存在某个property,如果复杂Condition的话,测试思路也是一样的。

例子3:测试@ConditionalOnProperty

Spring framework只提供了@Conditional,Spring boot对这个机制做了扩展,提供了更为丰富的@ConditionalOn*,这里我们以@ConditionalOnProperty举例说明。

先看FooConfiguration

@Configuration public class FooConfiguration {    @Bean   @ConditionalOnProperty(prefix = "foo", name = "create", havingValue = "true")   public Foo foo() {     return new Foo();   }  } 

FooConfigurationTest

public class FooConfigurationTest {    private AnnotationConfigApplicationContext context;    @BeforeMethod   public void init() {     context = new AnnotationConfigApplicationContext();   }    @AfterMethod(alwaysRun = true)   public void reset() {     context.close();   }    @Test(expectedExceptions = NoSuchBeanDefinitionException.class)   public void testFooCreatePropertyNull() {     context.register(FooConfiguration.class);     context.refresh();     context.getBean(Foo.class);   }    @Test   public void testFooCreatePropertyTrue() {     EnvironmentTestUtils.addEnvironment(context, "foo.create=true");     context.register(FooConfiguration.class);     context.refresh();     assertNotNull(context.getBean(Foo.class));   }    @Test(expectedExceptions = NoSuchBeanDefinitionException.class)   public void testFooCreatePropertyFalse() {     EnvironmentTestUtils.addEnvironment(context, "foo.create=false");     context.register(FooConfiguration.class);     context.refresh();     assertNotNull(context.getBean(Foo.class));   }  } 

这段测试代码和例子2的逻辑差不多,只不过例子2里使用了我们自己写的Condition,这里使用了Spring Boot提供的@ConditionalOnProperty。

并且利用了Spring Boot提供的[EnvironmentTestUtils][javadoc-spring-boot-EnvironmentTestUtils]简化了给Environment添加property的工作:

EnvironmentTestUtils.addEnvironment(context, "foo.create=false"); 

例子4:测试Configuration Properties

Spring Boot还提供了类型安全的Configuration Properties,下面举例如何对其进行测试。

BarConfiguration

@Configuration @EnableConfigurationProperties(BarConfiguration.BarProperties.class) public class BarConfiguration {    @Autowired   private BarProperties barProperties;    @Bean   public Bar bar() {     return new Bar(barProperties.getName());   }    @ConfigurationProperties("bar")   public static class BarProperties {      private String name;      public String getName() {       return name;     }      public void setName(String name) {       this.name = name;     }   }  } 

BarConfigurationTest

public class BarConfigurationTest {    private AnnotationConfigApplicationContext context;    @BeforeMethod   public void init() {     context = new AnnotationConfigApplicationContext();   }    @AfterMethod(alwaysRun = true)   public void reset() {     context.close();   }    @Test   public void testBarCreation() {     EnvironmentTestUtils.addEnvironment(context, "bar.name=test");     context.register(BarConfiguration.class, PropertyPlaceholderAutoConfiguration.class);     context.refresh();     assertEquals(context.getBean(Bar.class).getName(), "test");   }  } 

注意到因为我们使用了Configuration Properties机制,需要注册PropertyPlaceholderAutoConfiguration,否则在BarConfiguration里无法注入BarProperties。

参考文档

本文发表于2017年09月05日 10:36
(c)注:本文转载自https://my.oschina.net/sqq/blog/1529030,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1988 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1