Spring Boot 1.5.4: exclude configuration class in unit test - java

I have a Spring Boot project, version 1.5.4, with a MongoDb configuration class:
#Configuration
public class MongoConfig {
#Value("${spring.data.mongo.client.uri:mongodb://localhost:27017/database}")
private String mongoURI;
#Bean
public MongoDbFactory mongoFactory() throws UnknownHostException{
return new SimpleMongoDbFactory(new MongoClientURI(mongoURI));
}
#Bean
public MongoTemplate mongoTemplate() throws UnknownHostException, MongoException{
return new MongoTemplate(mongoFactory());
}
}
In my integration test i want use Embedded Mongo (https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo).
The problem is that the MongoDb configuration class start before the initialitation of Embedded mongo and try to connect to the database, so my test fail. If i remove the MongoConfig class, all test work well.
How can i exclude it only in my test execution?

Exclude the MongoDB autoconfiguration by using below annotation over your test class.
#EnableAutoConfiguration(exclude={MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class})
Then in the same path as your test class create a configuration class and define your mongo bean over there. This will get picked up during application start up
**#Configuration
public class MockConfigurations {
#Bean
#Primary
public MongoTemplate getMongoTemplate() {
//define your bean
return mongoTemplate;
}
}**

Please refer the answers here. It has two ways of excluding configurations.
Spring boot: apply #Configuration to certain package only
Update 1:
Alternatively, the most efficient way that I can think of is to use Spring profiles and load the profile for the tests
Define your TestConfiguration class and import it your test class.
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(MyTestConfiguration.class)
public class MyTests {
#Test
public void exampleTest() {
...
}
}
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-detecting-config
Update 2:
For EmbeddedMongoAutoConfiguration please refer the detailed answers here.
How do you configure Embedded MongDB for integration testing in a Spring Boot application?

I solved it with this configuration on my test class:
#RunWith(SpringRunner.class)
#ComponentScan({"it.app.server.dal","it.app.server.listener"})
#DataMongoTest() //mongoDB
public class ListenerTests {
...
}
The annotation #DataMongoTest() load my Embbedded MongoDb and with #ComponentScan i can just load the services and repositories wich i need in my test.

Related

Read #PropertySource without Spring Boot Application

I have a multi module Spring Boot project with the following structure:
MyAppModule
src/main/java/Application.java
...
IntegrationTests
src/test/java/integrationTest.java
The IntegrationTest module is testing the MyAppModule. The IntegrationTest module does not have a main package. Therefore there is no Spring Application. It has just the test package.
Nevertheless I would like to read in the application.yaml for some properties but I'm not able to because the attributes are always null:
#Configuration
#PropertySource("classpath:application.yaml")
public class IntegrationTest {
#Value("${app.url}")
private String appUrl;
}
Isn't it possible to use the #Value annotation without having a Spring Application (main with SpringApplication.run etc.)?
Why not just #ConfigurationProperties?
#Configuration
#ConfigurationProperties(prefix = "app")
public class IntegrationTest {
private String url;
}
Add Integration tests base package for component scanning in spring configuration.
#ComponentScan("basePackage1,basePackage2....etc")
Thanks for all responses. Solved it with the following code and a SpringBootApplication was necessary.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class IntegrationTest {
#Value("${app.url}")
private String url;
}

Spring Boot Exclude DataSource Configuration

I have a small application that when live, makes a database connection, and stores and persists some data.
I'm currently in the midsts of trying to write some tests, and I'm wanting to completely cut off the database part of the application, and just mock it in the tests.
The Datasource is setup with a configuration class.
#Component
#Configuration
public class DataSourceConfiguration {
#Bean
public DataSource myDataSource() { ... }
}
and a test boostrap that currently looks similar to
#RunWith(SpringRunner.class)
#EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class MyTest {
}
When running the test, I can see that Spring is trying to instantiate Hibernate, and a connection to the db, I assume because of my config class. How can I stop Spring from doing this?
No Need to use : #EnableAutoConfiguration
We can narrow down the tests to just the web layer by using #WebMvcTest as below,
#RunWith(SpringRunner.class)
#WebMvcTest
public class WebLayerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello World")));
}
}
Refer how to test spring application with only web or using complete application context loading : https://spring.io/guides/gs/testing-web/ refer mocking example : http://www.lucassaldanha.com/unit-and-integration-tests-in-spring-boot/

What is the preferred way to configure a Spring Boot Test application?

I have configured my application using #Configuration annotated classes in the config package:
main
java
com.ourcompany
config
PersistenceConfig
JacksonConfig
persistence
...
Application
test
java
com.ourcompany
persistence
PersistenceTest
The configuration gets picked up by the Application class without a problem:
#SpringBootApplication
public class Application {
public static void main( String[] args ) {
SpringApplication.run( Application.class, args );
}
}
However, the Test class:
#RunWith(SpringRunner.class)
#DataMongoTest
public class PersistenceTest {
...
}
does not pick up the configuration, unless I specify the name of the configuration class in the annotation:
#SpringBootTest(classes = PersistenceConfig.class)
I find this quite unintuitive, since I have to explicitly list all my config classes.
What is the idiomatic way to share the configuration in Spring Boot?
EDIT
In the end the problem was with including both #DataMongoTest and #SpringBootTest at the same time. Removing the #DataMongoTest and annotating the test class as:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
public class PersistenceTest {
...
}
Solved the problem. However, the question as to what is the best practice remains.

Overriding beans in Integration tests

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)
}
}
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
1.
You can use #Primary annotation:
#Configuration
public class MockRestTemplateConfiguration {
#Bean
#Primary
public RestTemplate restTemplate() {
return Mockito.mock(RestTemplate.class)
}
}
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;
#Autowired
private RestTemplate restTemplate;
#Autowired
private UsersClient usersClient;
#BeforeMethod
public void init() {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void testSingleGet() throws Exception {
// GIVEN
int testingIdentifier = 0;
mockServer.expect(requestTo(USERS_URL + "/" + testingIdentifier))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(TEST_RECORD0, MediaType.APPLICATION_JSON));
// WHEN
User user = usersClient.getUser(testingIdentifier);
// THEN
mockServer.verify();
assertEquals(user.getName(), USER0_NAME);
assertEquals(user.getEmail(), USER0_EMAIL);
}
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
If you want to customize the primary configuration, you can use a
nested #TestConfiguration class. Unlike a nested #Configuration class,
which would be used instead of your application’s primary
configuration, a nested #TestConfiguration class is used in addition
to your application’s primary configuration.
Example using SpringBoot:
Main class
#SpringBootApplication() // Will scan for #Components and #Configs in package tree
public class Main{
}
Main config
#Configuration
public void AppConfig() {
// Define any beans
}
Test config
#TestConfiguration
public void AppTestConfig(){
// override beans for testing
}
Test class
#RunWith(SpringRunner.class)
#Import(AppTestConfig.class)
#SpringBootTest
public void AppTest() {
// use #MockBean if you like
}
Note: Be aware, that all Beans will be created, even those that you override. Use #Profile if you wish not to instantiate a #Configuration.
#MockBean and bean overriding used by the OP are two complementary approaches.
You want to use #MockBean to create a mock and forget the real implementation : generally you do that for slice testing or integration testing that doesn't load some beans which class(es) you are testing depend on and that you don't want to test these beans in integration.
Spring makes them by default null, you will mock the minimal behavior for them to fulfill your test.
#WebMvcTest requires very often that strategy as you don't want to test the whole layers and #SpringBootTest may also require that if you specify only a subset of your beans configuration in the test configuration.
On the other hand, sometimes you want to perform an integration test with as many real components as possible, so you don't want to use #MockBean but you want to override slightly a behavior, a dependency or define a new scope for a bean, in this case, the approach to follow is bean overriding :
#SpringBootTest({"spring.main.allow-bean-definition-overriding=true"})
#Import(FooTest.OverrideBean.class)
public class FooTest{
#Test
public void getFoo() throws Exception {
// ...
}
#TestConfiguration
public static class OverrideBean {
// change the bean scope to SINGLETON
#Bean
#Scope(ConfigurableBeanFactory.SINGLETON)
public Bar bar() {
return new Bar();
}
// use a stub for a bean
#Bean
public FooBar BarFoo() {
return new BarFooStub();
}
// use a stub for the dependency of a bean
#Bean
public FooBar fooBar() {
return new FooBar(new StubDependency());
}
}
}
With #Primary annotation, Bean overriding works with Spring Boot 1.5.X but fails with Spring Boot 2.1.X it throw error:
Invalid bean definition with name 'testBean' defined in sample..ConfigTest$SpringConfig:..
There is already .. defined in class path resource [TestConfig.class]] bound
Please add below properties= which will instruct Spring explicitly to allow overriding, it is self explainatory.
#SpringBootTest(properties = ["spring.main.allow-bean-definition-overriding=true"])
UPDATE: You can add the same property in application-test.yml (file name depend upon what test profile name you are tests with)
Getting a little deeper into it, see my second answer.
I solved the Problem using
#SpringBootTest(classes = {AppConfiguration.class, AppTestConfiguration.class})
instead of
#Import({ AppConfiguration.class, AppTestConfiguration.class });
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
#SpringBootTest(classes = AppTestConfiguration.class)
instead of (not working)
#Import(AppTestConfiguration.class );
It is pretty wired to see that this is so different. Maybe some one can explain this. I could not find any good answers until now. You might think, #Import(...) is not picked up if #SpringBootTestsis 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.
I´ve declared an inner configuration class within my test because I wanted to overwrite just a single method
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class FileNotificationWebhookTest{
public static class FileNotificationWebhookTestConfiguration {
#Bean
#Primary
public FileJobRequestConverter fileJobRequestConverter() {
return new FileJobRequestConverter() {
#Override
protected File resolveWindowsPath(String path) {
return new File(path);
}
};
}
}
}
However,
Declaring the configuration in #SpringBootTest did not work:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = {FileNotificationWebhookTest.FileNotificationWebhookTestConfiguration.class})
or annotating the test configuration with #Configuration did not work:
#Configuration
public static class FileNotificationWebhookTestConfiguration {
}
and was leading to
Caused by: org.springframework.context.ApplicationContextException:
Unable to start web server; nested exception is
org.springframework.context.ApplicationContextException: Unable to
start ServletWebServerApplicationContext due to missing
ServletWebServerFactory bean.
What did work for me ( contrary to some other posts here) was using #Import
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#Import(FileNotificationWebhookTest.FileNotificationWebhookTestConfiguration.class)
class FileNotificationWebhookTest {
}
Using Spring: 5.3.3 with Spring-Boot-Starter: 2.4.2
#MockBean creates Mockito mock instead of production build.
If you do not want to use Mockito, but provide a replacement in some other way (i.e. by disabling some features of bean with feature toggles), I suggest using combination of #TestConfiguration (since Spring Boot 1.4.0) and #Primary annotation.
#TestConfiguration will load your default context and apply your #TestConfiguration piece in addition to it. Adding #Primary will force your mocked RestTemplate to be injected to it's dependents.
See simplified example below:
#SpringBootTest
public class ServiceTest {
#TestConfiguration
static class AdditionalCfg {
#Primary
#Bean
RestTemplate rt() {
return new RestTemplate() {
#Override
public String exec() {
return "Test rest template";
}
};
}
}
#Autowired
MyService myService;
#Test
void contextLoads() {
assertThat(myService.invoke()).isEqualTo("Test rest template");
}
}
This is super weird.
In my case, (Spring Boot 2.6.7), I could simply #Import MyTestConfiguration containing a custom #Primary #Bean into my #SpringBootTest, and everything worked.
Right until I needed to explicitly name my bean.
Then I suddenly had to resort to
#SpringBootTest(
properties = ["spring.main.allow-bean-definition-overriding=true"],
classes = [MyTestConfig::class],
)
Check this answer along with others provided in that thread.
It's about overriding bean in Spring Boot 2.X, where this option was disabled by default. It also has some ideas about how to use Bean Definition DSL if you decided to take that path.
The simplest solution I found was to set this property in application.properties:
spring.main.allow-bean-definition-overriding=true
This will enable overriding of beans.
Next, create a configuration class in test, and annotate your bean with:
#Bean
#Primary
This way, this bean will override your usual bean when running tests.

Initialize database only when running Test using Spring java based configuration

I have the code to create data source and run scripts to create schema and add in data using java based spring configuration. I want to run those scripts only in test mode. Is there any annotation that I need to specify on top of the database initializer bean that would do the job?
You can use #profile annotation for this, like:
#Configuration
#Profile("test_profile")
public class StandaloneDataConfig {
#Bean
public DataSource dataSource() {
// do data source creation/initialization
}
}
Then use:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class,
classes={StandaloneDataConfig.class})
#ActiveProfiles("test_profile")
public class TransferServiceTest {
#Autowired
private TransferService transferService;
#Test
public void testTransferService() {
// test the transferService
}
}
You can read this blog post from the Spring team to learn more details.

Categories