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核心原理剖析》