Chapter 7: 测试

    我们先写一个简单的@Configuration

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

    1. private AnnotationConfigApplicationContext context;
    2. @BeforeMethod
    3. public void init() {
    4. context = new AnnotationConfigApplicationContext();
    5. }
    6. @AfterMethod(alwaysRun = true)
    7. public void reset() {
    8. context.close();
    9. }
    10. @Test
    11. public void testFooCreation() {
    12. context.register(FooConfiguration.class);
    13. context.refresh();
    14. assertNotNull(context.getBean(Foo.class));
    15. }
    16. }

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

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

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

    1. public class DataSourceAutoConfigurationTests {
    2. private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    3. @Before
    4. public void init() {
    5. EmbeddedDatabaseConnection.override = null;
    6. EnvironmentTestUtils.addEnvironment(this.context,
    7. "spring.datasource.initialize:false",
    8. "spring.datasource.url:jdbc:hsqldb:mem:testdb-" + new Random().nextInt());
    9. }
    10. @After
    11. public void restore() {
    12. EmbeddedDatabaseConnection.override = null;
    13. this.context.close();
    14. }

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

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

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

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

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

    1. public class FooConfigurationTest {
    2. @BeforeMethod
    3. public void init() {
    4. context = new AnnotationConfigApplicationContext();
    5. @AfterMethod(alwaysRun = true)
    6. public void reset() {
    7. context.close();
    8. }
    9. @Test(expectedExceptions = NoSuchBeanDefinitionException.class)
    10. public void testFooCreatePropertyNull() {
    11. context.register(FooConfiguration.class);
    12. context.refresh();
    13. context.getBean(Foo.class);
    14. }
    15. @Test
    16. public void testFooCreatePropertyTrue() {
    17. context.getEnvironment().getPropertySources().addLast(
    18. new MapPropertySource("test", Collections.singletonMap("foo.create", "true"))
    19. );
    20. context.register(FooConfiguration.class);
    21. context.refresh();
    22. assertNotNull(context.getBean(Foo.class));
    23. }
    24. @Test(expectedExceptions = NoSuchBeanDefinitionException.class)
    25. public void testFooCreatePropertyFalse() {
    26. context.getEnvironment().getPropertySources().addLast(
    27. new MapPropertySource("test", Collections.singletonMap("foo.create", "false"))
    28. );
    29. context.register(FooConfiguration.class);
    30. context.refresh();
    31. assertNotNull(context.getBean(Foo.class));
    32. }
    33. }

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

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

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

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

    1. public class FooConfigurationTest {
    2. private AnnotationConfigApplicationContext context;
    3. @BeforeMethod
    4. public void init() {
    5. context = new AnnotationConfigApplicationContext();
    6. }
    7. @AfterMethod(alwaysRun = true)
    8. public void reset() {
    9. @Test(expectedExceptions = NoSuchBeanDefinitionException.class)
    10. public void testFooCreatePropertyNull() {
    11. context.register(FooConfiguration.class);
    12. context.refresh();
    13. context.getBean(Foo.class);
    14. }
    15. @Test
    16. public void testFooCreatePropertyTrue() {
    17. EnvironmentTestUtils.addEnvironment(context, "foo.create=true");
    18. context.register(FooConfiguration.class);
    19. context.refresh();
    20. assertNotNull(context.getBean(Foo.class));
    21. }
    22. @Test(expectedExceptions = NoSuchBeanDefinitionException.class)
    23. public void testFooCreatePropertyFalse() {
    24. EnvironmentTestUtils.addEnvironment(context, "foo.create=false");
    25. context.register(FooConfiguration.class);
    26. context.refresh();
    27. assertNotNull(context.getBean(Foo.class));
    28. }
    29. }

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

    并且利用了Spring Boot提供的简化了给Environment添加property的工作:

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

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

    BarConfigurationTest

    1. public class BarConfigurationTest {
    2. private AnnotationConfigApplicationContext context;
    3. @BeforeMethod
    4. public void init() {
    5. context = new AnnotationConfigApplicationContext();
    6. }
    7. @AfterMethod(alwaysRun = true)
    8. public void reset() {
    9. context.close();
    10. }
    11. @Test
    12. public void testBarCreation() {
    13. EnvironmentTestUtils.addEnvironment(context, "bar.name=test");
    14. context.register(BarConfiguration.class, PropertyPlaceholderAutoConfiguration.class);
    15. context.refresh();
    16. assertEquals(context.getBean(Bar.class).getName(), "test");
    17. }
    18. }