For my Spring-Boot app I provide a RestTemplate though a @Configuration file so I can add sensible defaults(ex Timeouts). For my integration tests I would like to mock the RestTemplate as I dont want to connect to external services - I know what responses to expect. I tried providing a different implementation in the integration-test package in the hope that the latter will override the real implementation , but checking the logs it`s the other way around : the real implementation overrides the test one.
How can I make sure the one from the TestConfig is the one used?
This is my config file :
@Configuration
public class RestTemplateProvider {
private static final int DEFAULT_SERVICE_TIMEOUT = 5_000;
@Bean
public RestTemplate restTemplate(){
return new RestTemplate(buildClientConfigurationFactory());
}
private ClientHttpRequestFactory buildClientConfigurationFactory() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(DEFAULT_SERVICE_TIMEOUT);
factory.setConnectTimeout(DEFAULT_SERVICE_TIMEOUT);
return factory;
}
}
Integration test:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestConfiguration.class)
@WebAppConfiguration
@ActiveProfiles("it")
public abstract class IntegrationTest {}
TestConfiguration class:
@Configuration
@Import({Application.class, MockRestTemplateConfiguration.class})
public class TestConfiguration {}
And finally MockRestTemplateConfiguration
@Configuration
public class MockRestTemplateConfiguration {
@Bean
public RestTemplate restTemplate() {
return Mockito.mock(RestTemplate.class)
}
}
Getting a little deeper into it, see my second answer.
I solved the Problem using
instead of
In my case the Test is not in the same package as the App. So I need to specify the AppConfiguration.class (or the App.class) explicit. If you use the same package in the test, than I guess you could just write
isntead of (not working)
It is pretty wired to see that this is so different. Maybe some one can explain this. I could not find any good answeres till now. You might think,
@Import(...)
is not picked up if@SpringBootTests
is present, but in the log the overriding bean shows up. But just the wrong way around.By the way, using
@TestConfiguration
instead@Configuration
also makes no difference.1. You can use
@Primary
annotation:BTW, I wrote blog post about faking Spring bean
2. But I would suggest to take a look at Spring RestTemplate testing support. This would be simple example: private MockRestServiceServer mockServer;
More examples can be found in my Github repo here
The Problem in your configuration is that you are using
@Configuration
for your test configuration. This will replace your main configuration. Instead use@TestConfiguration
which will append (override) your main configuration.46.3.2 Detecting Test Configuration
Example using SpringBoot:
Main class
Main config
Test config
Test class
Note: Using
@Import(AppTestConfig.class)
instead will not override an Bean fromAppConfig
. Somehow it will just be added, if the Bean is missing. Appreciate any link to an offical Dokumentation, that makes this clear.Since Spring Boot 1.4.x there is an option to use
@MockBean
annotation to fake Spring beans.Reaction on comment:
To keep context in cache do not use
@DirtiesContext
, but use@ContextConfiguration(name = "contextWithFakeBean")
and it will create separate context, while it will keep default context in cache. Spring will keep both (or how many contexts you have) in cache.Our build is this way, where most of the tests are using default non-poluted config, but we have 4-5 tests that are faking beans. Default context is nicely reused