• Chapter 2: Annotations - @TestConfiguration
    • 例子1:作为内部类
    • 例子2:对@Configuration的补充和覆盖
    • 例子3:避免@TestConfiguration被扫描到
    • 参考文档

    Chapter 2: Annotations - @TestConfiguration

    @TestConfiguration是Spring Boot Test提供的一种工具,用它我们可以在一般的@Configuration之外补充测试专门用的Bean或者自定义的配置。

    @TestConfiguration实际上是一种@TestComponent,@TestComponent是另一种@Component,在语义上用来指定某个Bean是专门用于测试的。

    需要特别注意,你应该使用一切办法避免在生产代码中自动扫描到@TestComponent。
    如果你使用@SpringBootApplication启动测试或者生产代码,@TestComponent会自动被排除掉,如果不是则需要像@SpringBootApplication一样添加TypeExcludeFilter

    1. //...
    2. @ComponentScan(excludeFilters = {
    3. @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    4. // ...})
    5. public @interface SpringBootApplication

    例子1:作为内部类

    @TestConfiguration和@Configuration不同,它不会阻止@SpringBootTest去查找机制(在Chapter 1: 基本用法 - 使用Spring Boot Testing工具 - 例子4提到过),正如@TestConfiguration的javadoc所说,它只是对既有配置的一个补充。

    所以我们在测试代码上添加@SpringBootConfiguration,用@SpringBootTest(classes=...)或者在同package里添加@SpringBootConfiguration类都是可以的。

    而且@TestConfiguration作为内部类的时候它是会被@SpringBootTest扫描掉的,这点和@Configuration一样。

    测试代码TestConfigurationTest:

    1. @SpringBootTest
    2. @SpringBootConfiguration
    3. public class TestConfigurationTest extends AbstractTestNGSpringContextTests {
    4. @Autowired
    5. private Foo foo;
    6. @Test
    7. public void testPlusCount() throws Exception {
    8. assertEquals(foo.getName(), "from test config");
    9. }
    10. @TestConfiguration
    11. public class TestConfig {
    12. @Bean
    13. public Foo foo() {
    14. return new Foo("from test config");
    15. }
    16. }
    17. }

    例子2:对@Configuration的补充和覆盖

    @TestConfiguration能够:

    1. 补充额外的Bean
    2. 覆盖已存在的Bean

    要特别注意第二点,@TestConfiguration能够直接覆盖已存在的Bean,这一点正常的@Configuration是做不到的。

    我们先提供了一个正常的@Configuration(Config):

    1. @Configuration
    2. public class Config {
    3. @Bean
    4. public Foo foo() {
    5. return new Foo("from config");
    6. }
    7. }

    又提供了一个@TestConfiguration,在里面覆盖了foo Bean,并且提供了foo2 Bean(TestConfig):

    1. @TestConfiguration
    2. public class TestConfig {
    3. // 这里不需要@Primary之类的机制,直接就能够覆盖
    4. @Bean
    5. public Foo foo() {
    6. return new Foo("from test config");
    7. }
    8. @Bean
    9. public Foo foo2() {
    10. return new Foo("from test config2");
    11. }
    12. }

    测试代码TestConfigurationTest:

    1. @SpringBootTest(classes = { Config.class, TestConfig.class })
    2. public class TestConfigurationTest extends AbstractTestNGSpringContextTests {
    3. @Qualifier("foo")
    4. @Autowired
    5. private Foo foo;
    6. @Qualifier("foo2")
    7. @Autowired
    8. private Foo foo2;
    9. @Test
    10. public void testPlusCount() throws Exception {
    11. assertEquals(foo.getName(), "from test config");
    12. assertEquals(foo2.getName(), "from test config2");
    13. }
    14. }

    再查看输出的日志,就会发现Auto Configuration已经关闭。

    例子3:避免@TestConfiguration被扫描到

    在上面的这个例子里的TestConfig是会被@ComponentScan扫描到的,如果要避免被扫描到,在本文开头已经提到过了。

    先来看一下没有做任何过滤的情形,我们先提供了一个@SpringBootConfiguration(IncludeConfig):

    1. @SpringBootConfiguration
    2. @ComponentScan
    3. public interface IncludeConfig {
    4. }

    然后有个测试代码引用了它(TestConfigIncludedTest):

    1. @SpringBootTest(classes = IncludeConfig.class)
    2. public class TestConfigIncludedTest extends AbstractTestNGSpringContextTests {
    3. @Autowired(required = false)
    4. private TestConfig testConfig;
    5. @Test
    6. public void testPlusCount() throws Exception {
    7. assertNotNull(testConfig);
    8. }
    9. }

    从这段代码可以看到TestConfig被加载了。

    现在我们使用TypeExcludeFilter来过滤@TestConfiguration(ExcludeConfig1):

    1. @SpringBootConfiguration
    2. @ComponentScan(excludeFilters = {
    3. @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class)
    4. })
    5. public interface ExcludeConfig1 {
    6. }

    再来看看结果(TestConfigExclude_1_Test):

    1. @SpringBootTest(classes = ExcludeConfig1.class)
    2. public class TestConfigExclude_1_Test extends AbstractTestNGSpringContextTests {
    3. @Autowired(required = false)
    4. private TestConfig testConfig;
    5. @Test
    6. public void test() throws Exception {
    7. assertNull(testConfig);
    8. }
    9. }

    还可以用@SpringBootApplication来排除TestConfig(ExcludeConfig2):

    1. @SpringBootApplication
    2. public interface ExcludeConfig2 {
    3. }

    看看结果(TestConfigExclude_2_Test):

    1. @SpringBootTest(classes = ExcludeConfig2.class)
    2. public class TestConfigExclude_2_Test extends AbstractTestNGSpringContextTests {
    3. @Autowired(required = false)
    4. private TestConfig testConfig;
    5. @Test
    6. public void testPlusCount() throws Exception {
    7. assertNull(testConfig);
    8. }
    9. }

    参考文档

    • Spring Framework Testing
    • Spring Boot Testing
    • Detecting test configuration
    • Excluding test configuration