Github地址
在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的代码:
首先,我们构造一个Context
然后,注册FooConfiguration
然后,refresh Context
最后,在测试方法结尾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:测试@ConditionalSpring 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才可以,在这里我们测试了三种情况:
没有配置foo.create=true
配置foo.create=true
配置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:测试@ConditionalOnPropertySpring 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简化了给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。
参考文档Conditionally include @Configuration classes or @Bean methods
Condition annotations
Type-safe Configuration Properties
Spring Framework Testing
Spring Boot Testing
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70351.html
摘要:因为只有这样才能够在测试环境下发现生产环境的问题,也避免出现一些因为配置不同导致的奇怪问题。而方法则能够不改变原有配置不提供新的配置的情况下,就能够关闭。 Github地址 在Chapter 1: 基本用法 - 使用Spring Boot Testing工具里提到: 除了单元测试(不需要初始化ApplicationContext的测试)外,尽量将测试配置和生产配置保持一致。比如如果生产...
摘要:源代码见需要注意的是,如果是专供某个测试类使用的话,把它放到外部并不是一个好主意,因为它有可能会被扫描到,从而产生一些奇怪的问题。 Github地址 既然我们现在开发的是一个Spring项目,那么肯定会用到Spring Framework的各种特性,这些特性实在是太好用了,它能够大大提高我们的开发效率。那么自然而然,你会想在测试代码里也能够利用Spring Framework提供的特...
摘要:地址前面一个部分讲解了如何使用工具来测试项目,现在我们讲解如何使用工具来测试项目。所以我们可以利用这个特性来进一步简化测试代码。因为只有这样才能够在测试环境下发现生产环境的问题,也避免出现一些因为配置不同导致的奇怪问题。 Github地址 前面一个部分讲解了如何使用Spring Testing工具来测试Spring项目,现在我们讲解如何使用Spring Boot Testing工具来测...
摘要:地址在使用工具中提到在测试代码之间尽量做到配置共用。本章将列举几种共享测试配置的方法我们可以将测试配置放在一个里,然后在测试或中引用它。也可以利用的及自定义机制,提供自己的用在测试配置上。 Github地址 在使用Spring Boot Testing工具中提到: 在测试代码之间尽量做到配置共用。...能够有效利用Spring TestContext Framework的缓存机制,Ap...
摘要:地址是提供的一种工具,用它我们可以在一般的之外补充测试专门用的或者自定义的配置。实际上是一种,是另一种,在语义上用来指定某个是专门用于测试的。所以我们在测试代码上添加,用或者在同里添加类都是可以的。 Github地址 @TestConfiguration是Spring Boot Test提供的一种工具,用它我们可以在一般的@Configuration之外补充测试专门用的Bean或者自定...
阅读 2338·2023-04-25 19:27
阅读 3431·2021-11-24 09:39
阅读 3872·2021-10-08 10:17
阅读 3379·2019-08-30 13:48
阅读 1907·2019-08-29 12:26
阅读 3103·2019-08-28 17:52
阅读 3509·2019-08-26 14:01
阅读 3513·2019-08-26 12:19