I work with a bunch of Spring Boot applications. They have a bunch of configuration classes that run at startup. Each application can exclude some classes from auto-configuration with the "#EnableAutoConfiguration(exclude=..." mechanism.
For instance, one of the applications has this annotation:
#EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
CassandraDataAutoConfiguration.class, CassandraRepositoriesAutoConfiguration.class })
These are excluded because this application doesn't use those frameworks. However, this application DOES use Hazelcast, so it does not include the "HazelcastAutoConfiguration.class" in this list.
Each application also has "component" tests that lie somewhere between unit tests and integration tests. They use the following annotations at a minimum:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
The point of a component test is to test the entire application, but with network dependencies mocked and stubbed. In this case, we have to prevent the application from making a Hazelcast connection. If I temporarily add "HazelcastAutoConfiguration.class" to the exclusion list, that makes the component test work, but of course, the application will then fail to work.
I need some way to make the application exclude the Hazelcast configurator in the component test, but include it in normal operation.
I believe there are multiple ways I could get this done. Could someone detail my options? I believe one of those options is "profiles", but I'm not sure how that would work here.
Update:
Initially, I thought I could make this happen by doing this:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"spring.autoconfigure.exclude = " +
"org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration"})
But I found it was still calling HazelcastClientConfiguration. Then I discovered that this configuration class is also referenced in CacheAutoConfiguration, so I added that class to the exclude list. That sort of worked, but now I get:
IllegalStateException: No CacheResolver specified, and no bean of type CacheManager found. Register a CacheManager bean or remove the #EnableCaching annotation from your configuration.
Am I at the end of my property-driven rope? Is there a simpleish way to get through this with test properties or annotation properties on the #SpringBootTest annotation?
Use a profile for tests by annotating the tests with #ActiveProfiles("test") and annotate the configuration classes with #Profile("!test").
For library configuration classes you could mock the bean:
#MockBean
private SomeClass someClass;
Here is some more on this topic: https://www.baeldung.com/java-spring-mockito-mock-mockbean
Related
I understand that using #SpringbootTest I raise whole spring contex during test, or In my case using #SpringBootTest(classes = SomeController.class) I raise only one bean -> SomeController. If this controller have some dependencies I need to mock them up. Using annotation #WebMvcTest(SoneController.class) I will (based on my knowledge) achieve the same goal.
Question is: Are there any differences between those two annotations used in provided example?
There's a clear difference between #SpringBootTest(classes = SomeController.class) and #WebMvcTest(SomeController.class).
#SpringBootTest(classes = SomeController.class) - starts a server (i.e like Tomcat) + spring application context with the component SomeController.class. In addition to the controller, you should normally specify the context configuration to successfully start the whole app (For ex: when you don't specify the classes, it falls back to #SpringBootApplication).
#WebMvcTest(SomeController.class) - only starts the web layer of the application with SomeController.class.
What's the difference?
#SpringBootTest tests are usually integration tests, you start the full spring-boot application and test against that black box. You can still tweak the application startup by providing configuration, properties, web server type etc in the annotation parameters.
But #WebMvcTest(SomeController.class) is usually a unit test for your controller. These are lightweight and fast. The dependencies like #Service classes are mocked in such tests.
This is a good starting point - https://spring.io/guides/gs/testing-web/
There are several subtle differences between these two ways.
But you will discover a part of them only randomly when you will encounter problems such as bean initialization exception during the spring boot context init or a NullPointerException rising during the test execution.
To make things simpler, focus on intention.
When you write that :
#SpringBootTest(classes = SomeController.class)
you will make Spring to init only the SomeController bean instance.
Is it desirable to test a controller ?
Probably no since you need a way to invoke the controller with a controller approach.
For that a MockMvc instance would help.
With WebMvcTest you get that bean additionally loaded in the test context.
So that way is preferable :
#WebMvcTest(SomeController.class)
public class SomeControllerTest{
#Autowired
private MockMvc mvc;
...
}
Of course you could get a similar behavior with #SpringBootTest and some additional classes but it will be just an overhead : the #WebMvcTest specialized annotation is enough.
At last why make the reading of the test class harder for your follower ?
By weaving a contrived way of using spring boot test annotation, chances are good to come there.
I think for answering your question enough just read the Javadoc for both of these annotations:
1. #WebMvcTest
Annotation that can be used for a Spring MVC test that focuses only on Spring MVC components.
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. #Controller, #ControllerAdvice, #JsonComponent, Converter/GenericConverter, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not #Component, #Service or #Repository beans).
By default, tests annotated with #WebMvcTest will also auto-configure Spring Security and MockMvc (include support for HtmlUnit WebClient and Selenium WebDriver). For more fine-grained control of MockMVC the #AutoConfigureMockMvc annotation can be used.
#SpringbootTest
Annotation that can be specified on a test class that runs Spring Boot based tests. Provides the following features over and above the regular Spring TestContext Framework:
Uses SpringBootContextLoader as the default ContextLoader when no specific #ContextConfiguration(loader=...) is defined.
Automatically searches for a #SpringBootConfiguration when nested #Configuration is not used, and no explicit classes are specified.
Allows custom Environment properties to be defined using the properties attribute.
Allows application arguments to be defined using the args attribute.
Provides support for different webEnvironment modes, including the ability to start a fully running web server listening on a defined or random port.
Registers a TestRestTemplate and/or WebTestClient bean for use in web tests that are using a fully running web server.
We have created a custom spring boot starter which holds several common feature reusable cross internal projects.
The starter is working fine as expected. However, while integration test, the projects using the starter are not able to find all beans created by the custom starter.
NB: The internal project are using in their integration test class the annotation #SpringBootTest in order to load the whole spring context.
without more information, I would say your custom spring-boot-starter has not the same namespace, so the components are not scanned from your internal Application.
Lets say your internal Project namespace is: com.hello.package.p1 and your Spring-boot-starter has the namespace: net.greeting.package.p1
Try perhaps something like this:
#SpringBootTest(classes = TestConfiguration.class)
and in your TestConfiguration.class:
#Configuration
#ComponentScan(basePackages = "net.greeting.package.p1")
public static class TestConfiguration {
// ...
}
Thanks for your answer.
When executing the internal project normally, I see clearly that they are getting the bean instances without the need to add any custom component scan. We have just added the custom starter to their dependencies. I don't see why it's not loaded while integration test. (I think that it should be reported to Spring community)
I have found a workaround which I consider not the best solution, by importing the CommonAutoConfiguration class to the internal project integration class.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { InternalProjectbootApplication.class })
#DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
#Import(CommonAutoConfiguration.class)
public class ArchiveUnitServiceITTest implements ServiceInterfaceIT {...
I'm trying to write a integration test for my SpringBoot microservice that interacts with another service inside the product ecosystem.
Since this kind of testing is consider function/integration testing (depending on what nomenclature you use) it is usually done on some development environment.
However, I wanted to test a basic interaction between my service and a STUB/dummy app which are connected with RPC (so not exactly a typical TestRestTemplate test).
I know that there is a way to embed a service while booting up the Spring Context but have never done it by myself.
Does anyone have any experience with the upper or maybe a few helpful links where I can explore.
I have used WireMock in tests to mock services external to what I want to test that communicate over HTTP.
My test class annotated with #SpringBootTest is also annotated with #ContextConfiguration. In the classes attribute #ContextConfiguration I explicitly specify the configuration classes required to set up the Spring Context for the test in question. Here I can also include additional configuration classes in which I create beans only used in the test. In test configuration classes I can also override beans for the purpose of the test, creating mock beans etc.
Note that Spring Boot 2.1 and later disables bean overriding by default. It can be enabled by setting the following property to true:
spring.main.allow-bean-definition-overriding=true
To set the property for a single test, use the #TestPropertySource annotation like this:
#TestPropertySource(properties = {
"spring.main.allow-bean-definition-overriding=true"
})
Is there a way to write unit tests to make sure spring boot API doesn't get started if a certain bean is failed to create. eg: failing to create datasource bean.
This code should do it for you:
#RunWith(SpringRunner.class)
#SpringBootTest
public class AnyAppNameApplicationTests {
#Test
public void contextLoads() {
}
}
From the docs:
Annotation that can be specified on a test class that runs Spring Boot
based tests. Provides the following features over and above the
regular Spring TestContext Framework:
Uses SpringBootContextLoader as the default ContextLoader when no specific #ContextConfiguration(loader=...) is defined.
Automatically searches for a #SpringBootConfiguration when nested #Configuration is not used, and no explicit classes are specified.
Allows custom Environment properties to be defined using the properties attribute.
Provides support for different webEnvironment modes, including the ability to start a fully running web server listening on a defined or
random port.
Registers a TestRestTemplate and/or WebTestClient bean for use in web tests that are using a fully running web server.
If you are using JUnit 4, don’t forget to also add
#RunWith(SpringRunner.class) to your test, otherwise the annotations
will be ignored. If you are using JUnit 5, there’s no need to add the
equivalent #ExtendWith(SpringExtension) as #SpringBootTest and the
other #…Test annotations are already annotated with it.
I have class which annotated these annotations:
#ContextConfiguration(locations = { "classpath:pathToXml.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
Can you explain what features I had after I added these annotation on my class ?
You'll get the features described in #WebAppConfiguration configuration javadoc.
WebApplicationContext mostly changes the way resources are loaded, i.e. resources with unspecifed resource prefix will be loaded from src/main/webapp or from the location in value parameter(they won't be available in Spring context otherwise, because usually webapp folder is not included to classpath) instead of classpath:.
Also you will be able to to test the code which uses other WebApplicationContext features- ServletContextAware beans, Session and Request bean scopes etc.
That means that you will be able to use Spring MVC Test Framework
So, battling with the need to read database creation scripts for the in-memory database from src/test/resources in tests annotated with #WebAppConfiguration, I noticed that in the mock application context created, the resourceLoader field was null. So, I created a custom ApplicationContextInitializer that sets this field to an AnnotationConfigApplicationContext instance.
Then all tests that need access to the regular resources just add something like #ContextConfiguration(initializers = { MyConfigurableContext.class })
Seems to do the trick, but obviously YMMV