Springboot Integration Test - Unwanted Mocks - java

I am trying to write integration test for SpringBoot application. code looks something like below
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {Application.class, MyTestConfig.class})
#ActiveProfile("test")
class MyIntegrationTest {
#Autowire
ServiceInterface serviceA;
}
I noticed that applicationContext loads some of the service beans as Mockito mocked object which really defeats the purpose of Integration test as it does not execute some of the code. Can anyone suggest what could be wrong here. Please note that some of the services being autowired correctly but some are being mocked. I do not see any logical reason why they behaves differently since they are implemented same way. I am using spring boot 2.0.3
Already tried.
Removed MyTestConfig.class but problem remains same. Even if I use #SpringBootTest(classes = {Application.class, MyProblematicServiceImpl.class}), It still returns mocked object wherever it is autowired. MyProblematicServiceImpl is empty class annotated with #Service.

Looking at the docs, if you set the webEnvironment setting on the SpringBootTest annotation to something other than MOCK, the default, then it will start up a real web environment.

Promoting from the comments, so it’s answered.
The Application results in a component scan, which is picking up a test config you have. You may have to exclude some test configurations.
Spring Boot provides #TestConfiguration to solve this issue.

Related

Getting NullPointerException on a Test Spring Boot application [duplicate]

To test a component/bean in a Spring Boot application, the testing part of the Spring Boot documentation provides much information and multiple ways :
#Test, #SpringBootTest, #WebMvcTest, #DataJpaTest and still many other ways.
Why provide so many ways ?
How decide the way to favor ?
Should I consider as integration tests my test classes annotated with Spring Boot test annotations such as #SpringBootTest, #WebMvcTest, #DataJpaTest ?
PS : I created this question because I noticed that many developers (even experienced) don't get the consequences to use an annotation rather than another.
TL-DR
write plain unit tests for components that you can straightly test without loading a Spring container (run them in local and in CI build).
write partial integration tests/slicing unit test for components that you cannot straightly test without loading a Spring container such as components related to JPA, controllers, REST clients, JDBC ... (run them in local and in CI build)
write some full integration tests (end-to-end tests) for some high-level components where it brings values (run them in CI build).
3 main ways to test a component
plain unit test (doesn't load a Spring container)
full integration test (load a Spring container with all configuration and beans)
partial integration test/ test slicing (load a Spring container with very restricted configurations and beans)
Can all components be tested in these 3 ways ?
In a general way with Spring any component can be tested in integration tests and only some kinds of components are suitable to be tested unitary(without container).
But note that with or without spring, unitary and integration tests are not opposed but complementary.
How to determine if a component can be plain tested (without spring) or only tested with Spring?
You recognize a code to test that doesn't have any dependencies from a Spring container as the component/method doesn't use Spring feature to perform its logical.
Take that FooService class :
#Service
public class FooService{
private FooRepository fooRepository;
public FooService(FooRepository fooRepository){
this.fooRepository = fooRepository;
}
public long compute(...){
List<Foo> foos = fooRepository.findAll(...);
// core logic
long result =
foos.stream()
.map(Foo::getValue)
.filter(v->...)
.count();
return result;
}
}
FooService performs some computations and logic that don't need Spring to be executed.
Indeed with or without container the compute() method contains the core logic we want to assert.
Reversely you will have difficulties to test FooRepository without Spring as Spring Boot configures for you the datasource, the JPA context, and instrument your FooRepository interface to provide to it a default implementation and multiple other things.
Same thing for testing a controller (rest or MVC).
How could a controller be bound to an endpoint without Spring? How could the controller parse the HTTP request and generate an HTTP response without Spring? It simply cannot be done.
1)Writing a plain unit test
Using Spring Boot in your application doesn't mean that you need to load the Spring container for any test class you run.
As you write a test that doesn't need any dependencies from the Spring container, you don't have to use/load Spring in the test class.
Instead of using Spring you will instantiate yourself the class to test and if needed use a mock library to isolate the instance under test from its dependencies.
That is the way to follow because it is fast and favors the isolation of the tested component.
Here how to unit-test the FooService class presented above.
You just need to mock FooRepository to be able to test the logic of FooService.
With JUnit 5 and Mockito the test class could look like :
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
#ExtendWith(MockitoExtension.class)
class FooServiceTest{
FooService fooService;
#Mock
FooRepository fooRepository;
#BeforeEach
void init{
fooService = new FooService(fooRepository);
}
#Test
void compute(){
List<Foo> fooData = ...;
Mockito.when(fooRepository.findAll(...))
.thenReturn(fooData);
long actualResult = fooService.compute(...);
long expectedResult = ...;
Assertions.assertEquals(expectedResult, actualResult);
}
}
2)Writing a full integration test
Writing an end-to-end test requires to load a container with the whole configuration and beans of the application.
To achieve that #SpringBootTest is the way :
The annotation works by creating the ApplicationContext used in your
tests through SpringApplication
You can use it in this way to test it without any mock :
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.junit.jupiter.api.Test;
#SpringBootTest
public class FooTest {
#Autowired
Foo foo;
#Test
public void doThat(){
FooBar fooBar = foo.doThat(...);
// assertion...
}
}
But you can also mock some beans of the container if it makes sense :
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.context.SpringBootTest;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
#SpringBootTest
public class FooTest {
#Autowired
Foo foo;
#MockBean
private Bar barDep;
#Test
public void doThat(){
Mockito.when(barDep.doThis()).thenReturn(...);
FooBar fooBar = foo.doThat(...);
// assertion...
}
}
Note the difference for mocking as you want to mock a plain instance of a Bar class (org.mockito.Mock annotation)and that you want to mock a Bar bean of the Spring context (org.springframework.boot.test.mock.mockito.MockBean annotation).
Full integration tests have to be executed by the CI builds
Loading a full spring context takes time. So you should be cautious with #SpringBootTest as this may make unit tests execution to be very long and generally you don't want to strongly slow down the local build on the developer's machine and the test feedback that matters to make the test writing pleasant and efficient for developers.
That's why "slow" tests are generally not executed on the developer's machines.
So you should make them integration tests (IT suffix instead of Test suffix in the naming of the test class) and make sure that these are executed only in the continuous integration builds.
But as Spring Boot acts on many things in your application (rest controllers, MVC controllers, JSON serialization/deserialization, persistence, and so for...) you could write many unit tests that are only executed on the CI builds and that is not fine either.
Having end-to-end tests executed only on the CI builds is ok but having also persistence, controllers or JSON tests executed only on the CI builds is not ok at all.
Indeed, the developer build will be fast but as drawback the tests execution in local will detect only a small part of the possible regressions...
To prevent this caveat, Spring Boot provides an intermediary way : partial integration test or the slice testing (as they call it) : the next point.
3)Writing a partial integration test focusing on a specific layer or concern thanks to slice testing
As explained in the point "Recognizing a test that can be plain tested (without spring))", some components can be tested only with a running container.
But why using #SpringBootTest that loads all beans and configurations of your application while you would need to load only a few specific configuration classes and beans to test these components?
For example why loading a full Spring JPA context (beans, configurations, in memory database, and so forth) to test the controller part?
And reversely why loading all configurations and beans associated to Spring controllers to test the JPA repository part?
Spring Boot addresses this point with the slice testing feature.
These are not as much as fast than plain unit tests (that is without container) but these are really much faster than loading a whole spring context.
So executing them on the local machine is generally very acceptable.
Each slice testing flavor loads a very restricted set of auto-configuration classes that you can modify if needed according to your requirements.
Some common slice testing features :
Auto-configured JSON Tests : #JsonTest
To test that object JSON serialization and deserialization is working
as expected, you can use the #JsonTest annotation.
Auto-configured Spring MVC Tests : #WebMvcTest
To test whether Spring MVC controllers are working as expected, use
the #WebMvcTest annotation.
Auto-configured Spring WebFlux Tests : #WebFluxTest
To test that Spring WebFlux controllers are working as expected, you
can use the #WebFluxTest annotation.
Auto-configured Data JPA Tests : #DataJpaTest
You can use the #DataJpaTest annotation to test JPA applications.
And you have still many other slice flavors that Spring Boot provides to you.
See the testing part of the documentation to get more details.
Note that if you need to define a specific set of beans to load that the built-in test slice annotations don't address, you can also create your own test slice annotation(https://spring.io/blog/2016/08/30/custom-test-slice-with-spring-boot-1-4).
4)Writing a partial integration test focusing on specific beans thanks to lazy bean initialization
Some days ago, I have encountered a case where I would test in partial integration a service bean that depends on several beans that themselves also depend on other beans.
My problem was that two deep dependency beans have to be mocked for usual reasons (http requests and a query with large data in database).
Loading all the Spring Boot context looked an overhead, so I tried to load only specific beans.
To achieve that, I annotation the test class with #SpringBootTest and I specified the classes attribute to define the configuration/beans classes to load.
After many tries I have gotten something that seemed working but I had to define an important list of beans/configurations to include.
That was really not neat nor maintainable.
So as clearer alternative, I chose to use the lazy bean initialization feature provided by Spring Boot 2.2 :
#SpringBootTest(properties="spring.main.lazy-initialization=true")
public class MyServiceTest { ...}
That has the advantage to load only beans used at runtime.
I don't think at all that using that property has to be the norm in test classes but in some specific test cases, that appears the right way.

How to unit test spring bean definitions?

I have defined several spring beans via #Bean.
#Bean
void SomeClass someClass() {
return new SomeClass();
}
But these are not covered by unit tests as I use #Mock to mock the classes, and beans are not instantiated in unit tests.
Now test coverage is failing because spring bean definition lines are not covered, is there any way to fix it ?
We use Jacoco coverage.
Note: I am not using #Configuration, rather passing the class to Application context
#Configuration files are normally excluded from code-coverage tests by rule. Running the application with #SpringBootTest (if you're using Boot) may also cause these to be marked as executed.

Disable Hazelcast instance while running JUnit test with ApplicationContext

I have such JUnit 5 test and becuase of test speed I need to disable creating Hazelcast instances while running test. Is there some way how to disable Hazelcast for one particular test?
#SpringBootTest
#RunWith(SpringRunner.class)
class MemoryTradeCommunicatorTest {
// test cases....
}
There are three main ways to approach your problem. I'll assume you just need to disable Hazelcast and that you're using Spring Boot though you don't mention it explicitly.
As Dmitrii mentions, you can mock the bean that provides the Hazelcast instance. Be aware that you'll probably need to split your configuration into multiple fragments so that you can import the beans required in your tests.
You can use Spring profiles. You'd need to create a test profile and create a mock Hazelcast instance inside it. The implementation depends a lot on how you create the real Hazelcast instance (Spring Boot or manually).
Spring Boot creates the Hazelcast instance because of auto-configuration classes. You can prevent this behavior by excluding specific classes. In your case, you'd launch your tests with spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration
You can use #Import for loading only required beans and mock your Hazelcast bean something like that:
#RunWith(SpringRunner.class)
#Import(value = {MemoryTradeCommunicator.class})
#MockBean
private CacheManager cacheManager;
class MemoryTradeCommunicatorTest {
// test cases....
}
Where CacheManager is your Hazelcast realization. Also, don't forget to declare your other needed bean into #Import

How to prevent spring boot from auto creating instance of bean 'entityManagerFactory' at startup?

I am working on a Spring boot application that uses Spring JPA with PostgreSQL. I am using #SpringBootTest(classes = <my package>.Application.class) to initialize my unit test for a controller class.
The problem is that this is causing the entityManagerFactory bean (and many other objects related to jpa, datasource, jdbc, etc.) to be created which is not needed for unit tests. Is there a way to prevent Spring from automatically creating these objects till they are actually used the first time?
I spent a lot of time trying to load up only the beans I need for my unit test but ran into many errors. I am relatively new to Spring and I am hoping someone else has run into this before...and can help. I can post code snippets if needed.
Update: I am not sure if I should edit or answer my own question...choosing to edit since I ended up changing my approach to unit tests. I added this to my test config class.
#Configuration
#ComponentScan(basePackages = {"api.controller", "api.config", "api.utils"})
public class TestControllerConfig {
}
and I mocked out the service and repository classes.
You can disable auto configuration in spring-boot using exclude attribute of #EnableAutoConfiguration, as follows:
#Configuration
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class TestConfig {
}
From #EnableAutoConfiguration documentation:
If the class is not on the classpath, you can use the excludeName attribute of the annotation and specify the fully qualified name instead. Finally, you can also control the list of auto-configuration classes to exclude via the spring.autoconfigure.exclude property.

How do I unit test a Spring 4 DAO method with Spring Security?

I had some unit tests for the DAO layer of a Spring 4 MVC application. Then I added Spring Security to certain methods in my controllers, and the DAO's they use.
I figured out how to make the #WithMockUser annotation work in the controller tests, but I'm stumped on the DAO tests. The exception I get on every dao test is:
java.lang.IllegalStateException: Failed to load ApplicationContext
....
Caused by: java.lang.IllegalArgumentException: An AuthenticationManager is required
I have these annoations at the start of the DaoTest:
#TestExecutionListeners({ WithSecurityContextTestExecutionListener.class })
#ContextConfiguration(classes = DaoConfig.class)
The #ContextConfiguration is the same as in the actual code; I've suggestions to make it different, but nothing concrete. How do I unravel this? I'm also hoping there's a way to do it without using org.springframework.web.* or org.springframework.test.web.* classes, since this should be 'underneath' the whole web tier.
In general it's not a good idea to test too many things at once. With a unit test you want to test one thing and one thing only. My advice would be to not have Spring Security load at all when your DAO unit tests run. You should be able to make your DAO unit test class extend some kind of AbstractTest class that loads a separate applicationContext-test.xml or Java Config that does not include Spring Security at all. That should allow your unit tests to not have to pierce some security layer the unit test really shouldn't care about. Unless, that is, you're in fact attempting integration testing instead. That would be another situation entirely.... Hope this helps.

Categories