Reputation: 31
I'm working on a project using spring boot, and we've just upgraded to version 1.4.0.RELEASE. As part of the version upgrade, we've started using the @SpringBootTest annotation on our abstract integration test class. After reading the documentation, it sounds like we should be able to use a nested @TestConfiguration-annotated config class to override bean definitions during specific tests. This is not working for us, and instead the non-test bean we are trying to override is still being used.
Interestingly, it seems like usages of the mock test bean and the production bean are actually intertwined within the same test, as if both beans exist side by side in the application context. Also, it seems like the order in which the integration tests run somehow affects this behaviour. I'm wondering if this is something we've misconfigured, or if there is something else going on.
edit:
The abstract class that the integration tests inherit from looks like this:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles({"local", "test"})
public abstract class BaseIntegrationTest {
@Value("${local.server.port}")
protected int port;
}
The integration test that we are seeing the strange behaviour in looks like this:
public class WebhookProcessorIT extends BaseIntegrationTest {
@TestConfiguration
public static class Config {
@Bean
@Primary
public WebhookTask webhookTask() {
return mock(WebhookTask.class);
}
}
// sometimes the mock above is used and sometimes
// the actual production bean is used
@Autowired
private WebhookTask task;
@Before
public void setup() {
when(task.process(any())).thenReturn(true);
}
// tests ...
}
And this is what the root application context class looks like:
@EnableAutoConfiguration(exclude = ErrorMvcAutoConfiguration.class)
@SpringBootApplication
public class Application {
private static final Logger log = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
final SpringApplication app = new SpringApplication(Application.class);
app.setBannerMode(Banner.Mode.OFF);
app.run(args);
}
}
edit:
I have also tried using @MockBean
, like so:
public class WebhookProcessorIT extends BaseIntegrationTest {
@MockBean
private WebhookTask task;
but I get the same result when the tests run. I can see that Spring is trying to override the production bean with the mock I am providing when I look at the logs during the test setup:
build 15-Sep-2016 09:09:24 2016-09-15 09:09:24 [34mINFO [0;39m [36m[DefaultListableBeanFactory][0;39m (main) Overriding bean definition for bean 'productionWebhookTask' with a different definition
however when it comes to test execution, i can still see the production bean being used:
build 15-Sep-2016 09:09:29 2016-09-15 09:09:29 [39mDEBUG[0;39m [36m[WebhookSupplier][0;39m (WebhookProcessor) Received webhook with ID '1234' from queue.
build 15-Sep-2016 09:09:30 2016-09-15 09:09:30 [39mDEBUG[0;39m [36m[WebhookSupplier][0;39m (WebhookProcessor) Received webhook with ID '5678' from queue.
build 15-Sep-2016 09:09:30 2016-09-15 09:09:30 [39mDEBUG[0;39m [36m[ProductionWebhookTask][0;39m (WebhookProcessor) Received webhook with ID '1234' for processing // production webhook task bean still being used for webhook '1234'
build 15-Sep-2016 09:09:30 2016-09-15 09:09:30 [39mDEBUG[0;39m [36m[WebhookSupplier][0;39m (WebhookProcessor) Deleting webhook with id '5678' from queue. // mock bean causes production logic to be skipped and we just delete webhook '5678'
// More logs from production webhook task operating on webhook with id '1234' and causing the test to fail
Upvotes: 3
Views: 2264
Reputation: 725
Since you use Spring Boot version 1.4.0, you can go on use the new introduced annotation @MockBean instead of using different configuration classes to mock your original beans. It's straight forward and very suitable to your use case.
Here you go with an example from the documentation
Upvotes: 0
Reputation: 725
Anyway, you can annotate your test beans with @Profile("test") and your real ones with @Profile("production")
and in your properties file put the property spring.profiles.active=test
from documentation
Unlike regular @Configuration classes the use of @TestConfiguration does not prevent auto-detection of @SpringBootConfiguration.
Unlike a nested @Configuration class which would be used instead of a your application’s primary configuration, a nested @TestConfiguration class will be used in addition to your application’s primary configuration.
Upvotes: 0