In my Spring boot 2.1 project I have different @Configuration
s for different test (ConfigurationA and ConfigurationB), that reside in different packages. Both configurations define the same set of beans but in a different manner (mocked vs. the real thing)
As I am aware of the Bean overriding mechanism introduced in Spring Boot 2.1, I have set the property: spring.main.allow-bean-definition-overriding=true
.
However I do have a test with the following the setup of the following configuration and test class. First there is a @Configuration
in the productive part (I'm using Maven):
package com.stackoverflow;
@Configuration
public class ProdConfiguration{
...
}
Then in the test branch there is a general Test @Configuration
on the same package level:
package com.stackoverflow
@Configuration
public class TestConfiguration {
@Bean
public GameMap gameMap() {
return Mockito.mock(GameMap.class);
}
}
And in a subpackage I have another @Configuration
:
package com.stackoverflow.impl;
@Configuration
public class RealMapTestConfiguration {
@Bean
public GameMap gameMap() {
return new GameMap("testMap.json");
}
}
And then of course there is the test that is troubling me:
package com.stackoverflow.impl;
@ExtendWith(SpringExtension.class)
@SpringBootTest
@ContextConfiguration(classes={RealMapTestConfiguration.class, ProdConfiguration.class})
@ActiveProfiles("bug") // spring.main.allow-bean-definition-overriding=true
public class MapImageServiceIT {
@Autowired
private GameMap map;
}
It turns out that the injected GameMap
into my test is a mock instance from TestConfiguration
instead of the real thing from RealMapTestConfiguration
. Aparrently in my test I have the configuration from ProdConfiguration
and TestConfiguration
, when I wanted ProdConfiguration
and RealMapTestConfiguration
. As the beans defined in the ProdConfiguration
and *TestConfiguration
are different the combination works, but TestConfiguration
and RealMapTestConfiguration
define the same been. It seems like the TestConfiguration
is picked up by component scanning as it is in the same package as ProdConfiguration
. I was under the impression that when overriding beans the bean definition that is closer to the test class would be preferred. However this seems not to be the case.
So here are my questions:
I've not used the spring.main.allow-bean-definition-overriding=true
property, but specifying specific config in a test class has worked fine for me as a way of switching between objects in different tests.
You say...
It turns out that the injected GameMap into my test is a mock instance from TestConfiguration instead of the real thing from RealMapTestConfiguration.
But RealMapTestConfiguration does return a mock
package com.stackoverflow.impl;
@Configuration
public class RealMapTestConfiguration {
@Bean
public GameMap gameMap() {
return Mockito.mock(GameMap.class);
}
}
I think the problem here is that including ContextConfiguration
nullifies (part of) the effect of @SpringBootTest
. @SpringBootTest
has the effect of looking for @SpringBootConfiguration
in your application (starting from the same package, I believe). However, if ContextConfiguration
is applied, then configurations are loaded from there.
Another way of saying that: because you have ContextConfiguration
in your test, scanning for @Configuration
classes is disabled, and TestConfiguration
is not loaded.
I don't think I have a full picture of your configuration setup so can't really recommend a best practice here, but a quick way to fix this is to add TestConfiguration
to your ContextConfiguration
in your test. Make sure you add it last, so that it overrides the bean definitions in the other two configurations.
The other thing that might work is removing @ContextConfiguration
entirely and letting the SpringBootApplication
scanning do its thing - that's where what you said about the bean definition that is closest may apply.
In that case just don't use @Configuration on configuration class and import it to the test manually using @Import, example:
@SpringBootTest
@Import(MyTest.MyTestConfig.class)
public class MyTest {
@Autowired
private String string;
@Test
public void myTest() {
System.out.println(string);
}
static class MyTestConfig {
@Bean
public String string() {
return "String";
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.