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
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/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或者自定...
閱讀 2361·2023-04-25 19:27
閱讀 3491·2021-11-24 09:39
閱讀 3906·2021-10-08 10:17
閱讀 3397·2019-08-30 13:48
閱讀 1930·2019-08-29 12:26
閱讀 3121·2019-08-28 17:52
閱讀 3537·2019-08-26 14:01
閱讀 3534·2019-08-26 12:19