Spring Boot:@TestConfiguration 在集成测试期间不覆盖 Bean

Posted

技术标签:

【中文标题】Spring Boot:@TestConfiguration 在集成测试期间不覆盖 Bean【英文标题】:Spring Boot: @TestConfiguration Not Overriding Bean During Integration Test 【发布时间】:2018-11-09 11:06:01 【问题描述】:

我在一个用@Configuration 装饰的类中定义了一个Bean

@Configuration
public class MyBeanConfig 
    @Bean
    public String configPath() 
        return "../production/environment/path";
    

我有一个用@TestConfiguration 装饰的类,它应该覆盖这个Bean

@TestConfiguration
public class MyTestConfiguration 
    @Bean
    @Primary
    public String configPath() 
        return "/test/environment/path";
    

configPath bean 用于设置外部文件的路径,该文件包含在启动期间必须读取的注册码。它用于@Component 类中:

@Component
public class MyParsingComponent 
    private String CONFIG_PATH;
    
    @Autowired
    public void setCONFIG_PATH(String configPath) 
        this.CONFIG_PATH = configPath;
    

在尝试调试时,我在每个方法以及测试配置类的构造函数中设置了一个断点。 @TestConfiguration 的构造函数断点被命中,所以我知道我的测试配置类实例化了,但是该类的 configPath 方法永远不会被命中。相反,普通@Configuration 类的configPath 方法被命中,MyParsingComponent 中的@Autowired String 始终是../production/environment/path,而不是预期的/test/environment/path

不知道为什么会这样。任何想法将不胜感激。

【问题讨论】:

你的测试类是否用@Import(MyTestConfiguration.class)注解? @SamBrannen 它不存在。并且添加它不起作用。但是将其更改为@ContextConfiguration(MyTestConfiguration.class) 确实如此。不过,仍然不明白为什么在没有 ContextConfiguration 的情况下会忽略 @Primary 注释。 如 Spring Boot 参考手册中所述,任何配置在带有 @TestConfiguration 注释的***类中的 bean 都不会通过组件扫描被拾取。所以这就是为什么你必须明确声明它。 如果用@TestConfiguration 注释的类是测试类中的静态嵌套类,它将被自动使用。 如果这回答了您的问题,我可以将其作为正式的答案。 ;-) 【参考方案1】:

如 Spring Boot 参考手册的 Detecting Test Configuration 部分所述,任何配置在带有 @TestConfiguration 注释的***类中的 bean 都将不会通过组件扫描被拾取。所以你必须明确注册你的@TestConfiguration 类。

您可以通过 @Import(MyTestConfiguration.class)@ContextConfiguration(classes = MyTestConfiguration.class) 在您的测试课程中执行此操作。

另一方面,如果您使用 @TestConfiguration 注释的类是 static 嵌套类您的测试类中,它将自动注册。

【讨论】:

我尝试使用#TestConfiguration 放置在***类上并使用#Import(MyTestConfiguration.class) 和不使用#TestConfiguration 但使用#Import(MyTestConfiguration.class) 进行测试。结果是平等的。是不是说#TestConfiguration没用了? @TestConfiguration 仍然有用。有关详细信息,请参阅链接文档。 网址已失效 由于 Spring Boot 2.2 文档的更改,我修复了链接。谢谢 我想为多个测试类重用@TestConfiguration,因此拥有一个所有其他人都继承的抽象测试类可能是一种选择。不幸的是,如果@TestConfiguration 在当前测试继承自的抽象类中,则不会拾取它。 @SamBrannen 是设计使然吗?如何在测试类之间共享@TestConfiguration【参考方案2】:

确保@Bean 工厂方法的方法名称与任何现有的 bean 名称都不匹配。我对方法名称有疑问,例如 config() 或(在我的情况下) prometheusConfig() 与现有的 bean 名称冲突。 Spring 静默跳过这些工厂方法,根本不调用它们/不实例化 bean。

如果您想在测试中覆盖 bean 定义,请在 @Bean("beanName") 注释中明确使用 bean 名称作为字符串参数。

【讨论】:

这是实际答案。我有一个带有 @TestConfiguration 的嵌套静态类,但它不起作用。问题是工厂方法的名称与实际的 bean 相同。 在我非常特殊的情况下,我还必须添加@Primary。否则它只会抛出expected single matching bean but found 2【参考方案3】: 测试配置必须通过@Import(MyTestConfiguration.class)显式导入到测试中。 @Configuration@TestConfiguration 中的@Bean 方法的名称必须不同。至少它在 Spring Boot v2.2 中有所不同。 还要确保 spring.main.allow-bean-definition-overriding=true 否则无法覆盖 bean。

【讨论】:

您的第二个和第三个项目符号语句相互冲突 - bean 覆盖机制意味着 bean 名称确实匹配。【参考方案4】:

对我来说,这个代码:

  @TestConfiguration // 1. necessary
  public class TestMessagesConfig 

    @Bean
    @Primary // 2. necessary
    public MessageSource testMessageSource()  // 3. different method name than in production code e.g. add test prefix

    
  

【讨论】:

【参考方案5】:

我最近遇到了一个类似的问题,并通过使用 @Primary 和 @Bean 注释我的测试 bean 来解决它。不知道为什么需要它,这似乎没有在 Spring 文档中记录。我的SpringBoot版本是2.0.3。

【讨论】:

我一直在寻找这个答案!谢谢,成功了【参考方案6】:

我遇到了一个相关的问题,即使我使用的是内部静态类,我的测试 bean 也没有被注册。

事实证明,您仍然需要将内部静态类添加到 @ContextConfiguration 类数组中,否则 @TestConfiguration 中的 bean 不会被拾取。

public interface Foo 
    String execute();

public class FooService 
    private final Foo foo;

    FooService(Foo foo) 
        this.foo = foo;
    

    public String execute() 
        return foo.execute();
    

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = FooService.class, FooTest.FooTestConfig.class)
public class FooTest 
    @Autowired
    FooService fooService;

    @Test
    void test() 
        Assertions.assertEquals("MY_TEST_BEAN", fooService.execute());
    

    @TestConfiguration
    static class FooTestConfig 
        @Bean
        public Foo getFooBean() 
            return () -> "MY_TEST_BEAN";
        
    

【讨论】:

以上是关于Spring Boot:@TestConfiguration 在集成测试期间不覆盖 Bean的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Spring Boot 应用程序 pom 同时需要 spring-boot-starter-parent 和 spring-boot-starter-web?

《02.Spring Boot连载:Spring Boot实战.Spring Boot核心原理剖析》

spring-boot-quartz, 依赖spring-boot-parent

spring-boot系列:初试spring-boot

Spring Boot:Spring Boot启动原理分析

Spring Boot:Spring Boot启动原理分析