英文:
Mocked named beans in Spring configuration without using allow-bean-definition-overriding?
问题
以下是翻译好的内容:
我有两个具有相同签名的 beans。它们被命名,以便为请求它们的类获取正确的实例。
@Configuration
public class MyConfiguration {
@Bean("durationForX")
public Duration durationForX() {
return Duration.ofSeconds(1);
}
@Bean("durationForY")
public Duration durationForY() {
return Duration.ofSeconds(5);
}
}
并且在以下情况下使用:
@Component
public class MyService {
public MyService(
@Qualifier("durationForX") duration
) {
...
}
}
Y的情况类似。
现在,我想在集成测试中自动装配上述 beans 的模拟。我尝试了以下方法:
@Configuration
@Profile("my-test-profile")
public class IntegrationTestConfiguration {
@Primary
@Bean("durationForX")
public Duration durationForXMock() {
return Duration.ofMillis(100);
}
@Primary
@Bean("durationForY")
public Duration durationForYMock() {
return Duration.ofMillis(500);
}
@Primary
@Bean
public AnotherService anotherService() {
// 这个可以正常工作,可能是因为它不是命名 bean
...
}
}
但是,在运行集成测试时,会出现以下错误消息:
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'durationForX', defined in class path resource [com/../../MyConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [com/.../.../IntegrationTestConfiguration.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
我不是在集成测试中自动装配实例本身,而只是在应用程序中调用一个入口点。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {MyApp.class})
@ActiveProfiles("it")
class MyIntegrationTest {
@Autowired
GraphQLTestTemplate graphQL;
...
}
我不太愿意将 bean 覆盖设置为 true,因为我想控制在哪里使用哪些 bean。我期望对命名的 beans 进行模拟应遵循与非命名 bean 相同的模式,为什么会出现这种情况?有关解决方法的任何想法吗?
英文:
I have two beans with the same signature. They are named in order to get the correct instance to the classes requesting them.
@Configuration
public class MyConfiguration {
@Bean("durationForX")
public Duration durationForX() {
return Duration.ofSeconds(1);
}
@Bean("durationForY")
public Duration durationForY() {
return Duration.ofSeconds(5);
}
}
and used as
@Component
public class MyService {
public MyService(
@Qualifier("durationForX") duration
) {
...
}
}
and similar for Y.
Now, I want to have mocks of the above beans autowired in an integration test. I've tried the following
@Configuration
@Profile("my-test-profile")
public class IntegrationTestConfiguration {
@Primary
@Bean("durationForX")
public Duration durationForXMock() {
return Duration.ofMillis(100);
}
@Primary
@Bean("durationForY")
public Duration durationForYMock() {
return Duration.ofMillis(500);
}
@Primary
@Bean
public AnotherService anotherService() {
// This one works as expected, probably because it is not a named bean
...
}
}
which, when running the integration tests, results in the error message
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'durationForX', defined in class path resource [com/../../MyConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [com/.../.../IntegrationTestConfiguration.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
I'm not auto-wiring the instances themselves in the integration tests, only one entry point for the application in order to call it.
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, classes = {MyApp.class})
@ActiveProfiles("it")
class MyIntegrationTest {
@Autowired
GraphQLTestTemplate graphQL;
...
}
I'm not too keen on setting the bean override to true as I want to be in control of which beans are used where. I would expect mocking the named beans to follow the same pattern as the not named one, why is this? Any idea on workarounds?
答案1
得分: 1
我建议在测试时使用不同的配置文件,例如您可以在主应用程序的主 application.yml
文件中定义以下值:
application.yml
duration1: 1
duration2: 5
然后在 MyConfiguration
类中使用 @Value
注解来读取它们:
@Configuration
public class MyConfiguration {
@Value("${duration1}")
private Integer duration1;
@Value("${duration2}")
private Integer duration2;
@Bean("durationForX")
public Duration durationForX() {
return Duration.ofSeconds(duration1);
}
@Bean("durationForY")
public Duration durationForY() {
return Duration.ofSeconds(duration2);
}
}
现在,为了进行测试,在 src/main/resources
或 src/test/resources
下创建 application-test.yml
文件,然后添加测试值的属性:
application-test.yml
duration1: 100
duration2: 500
无需额外的 IntegrationTestConfiguration
文件,您可以将测试属性维护在 test.yml
文件中。
注意: 确保您的测试类使用 @Profile("test")
和 @SpringBootTest
进行注解,以便加载带有相应测试属性的测试应用程序上下文:
@SpringBootTest
@Profile("test")
public class AppTest {
}
英文:
I would recommend using the different profile for test, for example you can define the values in main application.yml
for main application
application.yml
duration1:1
duration2:5
And then in read them in MyConfiguration
class using @Value
annotation
@Configuration
public class MyConfiguration {
@Value("${duration1})
private Integer duration1;
@Value("${duration2})
private Integer duration2;
@Bean("durationForX")
public Duration durationForX() {
return Duration.ofSeconds(duration1);
}
@Bean("durationForY")
public Duration durationForY() {
return Duration.ofSeconds(duration2);
}
}
Now for test create application-test.yml
under src/main/resources
or src/test/resources
, then add the properties with test values
application-test.yml
duration1:100
duration2:500
No need of any IntegrationTestConfiguration
file's you can just maintain test properties in test.yml
file
Note : Make sure you annotate test class with @Profile("test")
and @SpringBootTest
to load the test ap[plication context with corresponding test properties
@SpringBootTest
@Profile("test)
public class AppTest {
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论