I am very new to Spring Boot and trying to learn how testing should be done in Spring Boot. I read about the #SpringBootTest annotation which helps in integration testing an application. I was wondering how unit testing should be done in spring boot. Does unit testing require specifying the #SpringBootTest annotation or is that to be used only for integration testing? Are there specific annotations to be used for unit testing?
Any pointers would be much appreciated. Thanks in advance!
Edit:
Is the SpringBootTest annotation used only for integration testing? I found the following code example in the Spring documentation:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class HelloControllerTest {
#Autowired
private MockMvc mvc;
#Test
public void getHello() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("Greetings from Spring Boot!")));
}
}
Is this a unit or integration test? My guess is its not an integration test since it uses a MockMvc. Is that right? If so, does this mean that the #SpringBootTest annotation can be used for tests which are not full fledged integration tests?
Strictly speaking, "unit" tests should not use Spring. Just use JUnit/TestNG/Spock/whatever, like you normally would, to test the individual classes. #SpringBootTest is for integration, and beyond, tests.
This is what I would do:
For Integration Testing end-to-end:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ApplicationTests {
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private CarRepository repository;
#Autowired
private CarService carService;
#Test
public void contextLoads() {
}
#Test
public void basicTest() {
String body = this.restTemplate.getForObject("/", String.class);
assertThat(body).contains("Not Found");
}
}
For testing a controller only (controller unit test):
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = CarController.class, excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
public class ControllerTests {
#Autowired
private MockMvc mvc;
#MockBean
private CarRepository carRepository;
#MockBean
private MongoTemplate mongoTemplatel;
#Test
public void testGet() throws Exception {
short year = 2010;
given(this.carRepository.findByVin("ABC"))
.willReturn(new Car("ABC", "Honda", "Accord", year, 100000, "Red", "Ex-V6"));
this.mvc.perform(get("/car").param("VIN", "ABC").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andExpect(content().string("[{\"vin\":\"ABC\",\"make\":\"Honda\",\"model\":\"Accord\",\"year\":2010,\"mileage\":100000,\"color\":\"Red\",\"trim\":\"Ex-V6\",\"type\":null,\"maintenanceTasksList\":[\"Oil Change\",\"Tire Rotation\"]}]"));
}
}
You can find a complete Spring Boot application that include Integration and Unit tests here.
Related
According to the documentation there are two ways to do this:
First:
#RunWith(SpringRunner.class)
#WebAppConfiguration
#ContextConfiguration("my-servlet-context.xml")
public class MyWebTests {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
// ...
}
This form uses the actual context of the application.
And the second way:
public class MyWebTests {
private MockMvc mockMvc;
#Mock
private MyService myService;
#InjectMocks
private MyController myController;
#Before
public void setup() {
// Process mock annotations
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(myController)
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver()).build();
}
#Test
public void testsForPost() throws Exception {
Foo foo = new Foo();
//given
given(myService.findById(Matchers.anyLong())).willReturn(foo);
//when
this.mockMvc.perform((post("/foo")))
//then
.andExpect(status().isMethodNotAllowed())
.andDo(print());
}
...
}
With this method I am not using the application context.
My question is, can I consider the first form as integration tests? And the second as unit tests?
Otherwise what would be the best solution to do unit tests of SpringMVC.
I, too, would consider the first an integration test and the second a unit test.
I think a well written MVC controller should be covered with an integration test and not a unit test. A controller should only orchestrate calls to mappers, services, repositories and the like. Since the mappers, services, repositories and the like should be covered with their own unit tests, you don't gain a lot by unit testing your controller.
An integration test is much more valuable in this case, since it tests the whole interaction between the controller and components it orchestrates. And using tools like DBUnit it is not much harder to write.
I'm porting an app across from JDBC / REST to spring-data-rest and my one and only unit test with fails with the error
NoSuchBeanDefinitionException:
No qualifying bean of type 'com.xxx.repository.ForecastRepository' available
The app was retro-fitted with spring-boot just previously, and now I'm trying to put a new layer in place with spring-data-rest on top of spring-data-jpa.
I'm attempting to work out the correct Java-config according to
Custom Test Slice with Spring Boot 1.4
but I had to deviate from the idiomatic approach because
the #WebMvcTest annotation doesn't suppress the security module which causes the test to fail
the #MockMvcAutoConfiguration fails due to missing dependencies unless I specify #SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) (see here)
#WebMvcTest and #SpringBootTest are mutually exclusive since they both specify #BootstrapWith and can't run together
So this is the closest I've got but Spring can't locate my #RepositoryRestResource repository:
Repository
#RepositoryRestResource(collectionResourceRel = "forecasts", path = "forecasts")
public interface ForecastRepository extends CrudRepository<ForecastExEncoded,
Long> {
JUnit Test
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK,
classes = {TestRestConfiguration.class})
public class ForecastRestTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private ForecastRepository forecastRepository;
#Before
public void deleteAllBeforeTests() throws Exception {
forecastRepository.deleteAll();
}
#Test
public void shouldReturnRepositoryIndex() throws Exception {
mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk()).andExpect(
jsonPath("$._links.forecasts").exists());
}
}
Configuration
#OverrideAutoConfiguration(enabled = false)
#ImportAutoConfiguration(value = {
RepositoryRestMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
WebMvcAutoConfiguration.class,
MockMvcAutoConfiguration.class,
MockMvcSecurityAutoConfiguration.class
})
#Import({PropertySpringConfig.class})
public class TestRestConfiguration {}
Also tried...
I tried to configure the unit test with just #WebMvcTest and this #ComponentScan below from How to exclude AutoConfiguration from Spring Boot), in an attempt to simplify it all, however the excludeFilters had no effect.
#ComponentScan(
basePackages="com.xxx",
excludeFilters = {
#ComponentScan.Filter(type = ASSIGNABLE_TYPE,
value = {
SpringBootWebApplication.class,
JpaDataConfiguration.class,
SecurityConfig.class
})
})
I've set Spring's logging to trace because all I can do at this point is try to find clues as to what is happening from log output. So far though without any luck.
I can see in the logging that RepositoryRestConfiguration is loading, but obviously it isn't fed with the right info and I am unable to work out how that is done, after googling and pouring over the Spring docs and API. I think I must have read every relevant question here on SO .
Update 2016-11-16 10:00
One thing I see in the logs which concerns me is this:
Performing dependency injection for test context [DefaultTestContext#2b4a2ec7 [snip...]
classes = '{class com.xxx.TestRestConfiguration,
class com.xxx.TestRestConfiguration}',
i.e. the context lists the configuration class twice. I specified the config class (once only) on the #SpringBootTest#classes annotation. But if I leave off the #classes from the annotation, Spring Boot finds and pulls in all the config via the #SpringBootApplication class.
So is that a hint that I am specifying the configuration in the wrong place? How else would I do it?
After way too much time, I settled on this approach.
Custom Test Slice with Spring Boot 1.4 looked promising but I couldn't get anywhere with it.
While going over and over
Accessing JPA Data with REST
I realised I had to include the JPA setup because spring-data-rest is using them directly - no chance to mock them or run unit tests without an embedded database.
At least not as far as I understand it. Maybe it is possible to mock them and have spring-data-rest run on the mocks against test data, but I think spring-data-rest and spring-data are probably too tightly coupled.
So integration testing it must be.
In the Spring source code provided with the articles above
gs-accessing-data-rest/ApplicationTests.java
the logging shows Spring Boot pulling in the whole configuration for the application context.
So that my SpringBootApplication class is avoided and the security module isn't loaded up, I set up my tests like this:
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes = {
JpaDataConfiguration.class,
TestJpaConfiguration.class,
TestRestConfiguration.class,
PropertySpringConfig.class})
public class ForecastRestTests {
#SuppressWarnings("SpringJavaAutowiringInspection")
#Autowired
private MockMvc mockMvc;
#Autowired
private ForecastRepository forecastRepository;
#Before
public void deleteAllBeforeTests() throws Exception {
forecastRepository.deleteAll();
}
#Test
public void shouldReturnRepositoryIndex() throws Exception {
mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk()).andExpect(
jsonPath("$._links.forecasts").exists());
}
}
with these configuration classes:
#Configuration
#EnableJpaRepositories(basePackages = {"com.bp.gis.tardis.repository"})
#EntityScan(basePackages = {"com.bp.gis.tardis.type"})
public class JpaDataConfiguration {
and
#Configuration
#OverrideAutoConfiguration(enabled = false)
#ImportAutoConfiguration(value = {
CacheAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
TransactionAutoConfiguration.class,
TestDatabaseAutoConfiguration.class,
TestEntityManagerAutoConfiguration.class })
public class TestJpaConfiguration {}
and
#Configuration
#OverrideAutoConfiguration(enabled = false)
#ImportAutoConfiguration(value = {
RepositoryRestMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
WebMvcAutoConfiguration.class,
MockMvcAutoConfiguration.class,
MockMvcSecurityAutoConfiguration.class
})
public class TestRestConfiguration {}
so the TL;DR summary is: use #ContextConfiguration to specify the configuration files that specify #OverrideAutoConfiguration and #ImportAutoConfiguration
While trying to write test cases for a class whose functionality deals more with boiler plate code than business logic. I started wondering if unit testing is really worth for this class. But then again, when using TDD, we are advised to write test for any piece of logic we add.
As an example the below class, just uses DI to inject dependencies and get config parameters to set up the running of the application. Other than unit testing if dependencies are correctly injected, or if destroy is called when running finishes(which would be unit testing the java CDI framework than my own code), what else I can unit test?
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
#Singleton
#Startup
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class PipsAlprConnectionRunner {
#Inject
private PipsAlprConfiguration config;
#Inject
private PipsAlprConnector connector;
#Inject
private Scheduler scheduler;
#Inject
#PipsAlprAdapterService
private ServiceStatus status;
private Timer connectorTimer = null;
#PostConstruct
public void initialized() {
status.started();
connectorTimer = scheduler.schedule(connector, 0,
1000 * config.getPollPeriodSeconds());
status.operational();
}
#PreDestroy
public void destroy() {
connectorTimer.cancel();
connector.shutdown();
status.stopped();
}
}
I was unable to think of any testing scenarios to utilize TDD, on the above class, so just came up with the code, and now i am wondering what exactly can i unit test here.
Well, a case can be made that the class does something. It changes a status, it starts a timer. You can inject mocks of these objects into the class via Mockito and then make sure, that initialized() and destroy() both do what you expect them to do to these mocks.
#RunWith(MockitoJUnitRunner.class)
public class PipsAlprConnectionRunner {
#Mock
private PipsAlprConfiguration config;
#Mock
private PipsAlprConnector connector;
#Mock
private Scheduler scheduler;
#Mock
private ServiceStatus status;
#InjectMocks
private PipsAlprConnectionRunner pipsAlprConnectionRunner ;
#Test
public void initialized_should_set_status_started() {
pipsAlprConnectionRunner.initialized();
Mockito.verify(status).started();
}
// etc.
}
It's pretty much a question of personal taste if you want to create one method per "point of failure" or one method per method/test.
Personally, I would say that the goal is 100% coverage, so even pretty simple classes that mainly delegate should be covered. What happens if someone changes anything? The test ensures that such changes will not break existing functionality.
I want to unit test a java class with an autowired final class object, as well as another autowired class that has #PostConstruct method. While it is possible to test them individually, i am not able to combine them together.
This question is an extension to the question on injecting mockito mocks into spring bean
Code to be tested
public class A {
#Autowired
private FinalClass serviceClient;
#Autowired
private ClassWithPostConstructor resourceVerifier;
//no setters or constructors
public String useBothFinalClassAndClassWithPostConstructor() {
//logic to be tested
}
}
Working Test class
#RunWith(PowerMockRunner.class)
#PrepareForTest(FinalClass.class)
public class ATest {
//#org.mockito.Mock //fails to mock final class
#org.powermock.api.easymock.annotation.Mock
private FinalClass serviceClient;
#org.powermock.api.easymock.annotation.Mock
private ClassWithPostConstructor resourceVerifier;
//other mock objects required for mocking the services
//#InjectMocks //fails since mocking final class
private A a;
#Before
public void init() {
a = new A();
//working snippet with setters created in A and without #Autowired here within the test
serviceClient = PowerMock.create(FinalClass.class);
a.setServiceClient(serviceClient);
resourceVerifier = PowerMock.create(ClassWithPostConstructor.class);
a.setClassWithPostConstructor(resourceVerifier);
}
#Test
public void testTheMethodUsingExpectAndVerify() {
//test the functionality here
EasyMock.expect(serviceClient.callService()).andReturn("someMock");
EasyMock.expect(resourceVerifier.verifyFn()).andReturn("success");
PowerMock.replayAll();
A.useBothFinalClassAndClassWithPostConstructor();
PowerMock.verifyAll();
}
}
The above code works with the need for setters in file
Expected Test class
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"file:spring-configuration/unit-testing-config.xml"})
#PrepareForTest(FinalClass.class)
public class ATest {
#Autowired
private FinalClass serviceClient;
#Autowired
private ClassWithPostConstructor resourceVerifier;
//other mock objects required for mocking the services
private A a;
#Before
public void init() {
a = new A();
}
#Test
public void testTheMethodUsingExpectAndVerify() {
//test the functions here
EasyMock.expect(serviceClient.callService()).andReturn("someMock");
EasyMock.expect(resourceVerifier.verifyFn()).andReturn("success");
PowerMock.replayAll();
A.useBothFinalClassAndClassWithPostConstructor();
PowerMock.verifyAll();
}
}
//spring-configuration/unit-testing-config.xml
//same error even on customer factory
<bean id="resourceVerifier" class="org.powermock.api.easymock.PowerMock" factory-method="createMock">
<constructor-arg type="java.lang.Class" value="com.company...resourceVerifier" />
</bean>
<bean id="resourceVerifier" class="org.powermock.api.easymock.PowerMock" factory-method="createMock">
<constructor-arg type="java.lang.Class" value="com.company...serviceClient" />
</bean>
The above snippet mocks finalClass but calls #PostConstructor of ResourceVerifier.class - What should be done here to overcome this call.
Investigations
It is possible to test autowired files using #InjectMocks without the need for spring context configurations.
#InjectMock fails silently for static and final fields and when failing, it doesn't inject other mocks as well.
It is possible to mock final class using PowerMock's createMock and run the test with PowerMockRunner and #PrepareForTest. But this requires new unnecessary setters to inject the mocks for #Autowired fields.
MockitoAnnotations.#Mock doesn't work along well with PowerMock (especially when mocking final class objects) and can be solved via EasyMock.Annotations.#Mock
EasyMock and PowerMock doesn't have an #InjectMocks annotation to inject the mocks as possible by Mockito (Would have solved the problem in secs).
It is possible to inject autowired spring beans via SpringJUnit4Runner and a separate unit-testing #ContextConfiguration
It is possible to run the same test file with both PowerMockRunner and SpringJUnit4Runner with PowerMockRunnerDelegate
I know that #PostConstruct method will not be executed automatically if mocked in code than by using spring bean creation and injection.
If a wrapper factory bean class is written and used to create the mocks, it injects automatically, but calls the #PostConstruct method as well along with it.
It is not possible to depend on Springockito since it is unreliable at this stage.
But none of these worked since the usecase is a combination of all these.
Possible Solutions
Remove #Autowired fields and use Setter injections so that it is possible by mocking normally using PowerMock(Tested to work) - But it is a convention followed by external team packages - i should try my best to stick to it.
Or set #Autowired to the setters or to constructor
Alternatives?
I don't feel that the classes require reorganisation as they serve their purposes and are well designed as well.
Any other means that doesn't require to keep hands on the class under test - What if i didn't have the permissions to modify this class? i.e., a pure testing library dependent solution.
Not sure whether it is possible by PowerMockito? Haven't tried the combination of PowerMockito with PowerMock.
Hmm,
Not sure whether it is possible by PowerMockito? Haven't tried the combination of PowerMockito with PowerMock.
Seems to me you have a mess in your head and misunderstanding PowerMock/Mockito and EasyMock.
You should never use at same time PowerMockito and PowerMock, because these two classes are PowerMock friendly API for two different mocking frameworks: EasyMock and Mockito. And there is no reason to use they both.
And of course this want work
//#org.mockito.Mock //fails to mock final class
#org.powermock.api.easymock.annotation.Mock
private FinalClass serviceClient;
#org.powermock.api.easymock.annotation.Mock
private ClassWithPostConstructor resourceVerifier;
//other mock objects required for mocking the services
//#InjectMocks //fails since mocking final class
private A a;
Because, you decelerate and create mocks via EasyMock API, but tries to inject it with Mockito Annotation.
You have to choose only one Mocking Framework and use suitable API for it. For now, Mockito + PowerMockito (PowerMock API for Mockito) better fit your requirements.
You may full example how it works on PowerMock github
#RunWith(PowerMockRunner.class)
#PrepareForTest(FinalClass.class)
public class SpringInjectFinalClassExampleTest {
#Mock
private FinalClass finalClass;
#InjectMocks
private MyBean myBean = new MyBean();;
#Test
public void testInjectFinalClass() {
final String value = "What's up?";
when(finalClass.sayHello()).thenReturn(value);
assertEquals(value, myBean.sayHello());
}
}
I have a project with CDI and I would like to create unit test with mocks.
To manage mocks, I would like to use EasyMock and to run with CDI, I find the cdi-unit project which seem easy to use.
I have a problem to get a mock with EasyMock in CDI context. Here is my unit test:
#RunWith(CdiRunner.class)
#AdditionalClasses(MockFactory.class)
public class ResultYearMServiceImplTest {
#Inject
private IStockDao stockDao;
#Inject
private ResultYearMServiceImpl resultYearMService;
#Test
public void getResultList() {
EasyMock.reset(stockDao);
EasyMock.expect(stockDao.getListStocks()).andReturn(null).once()
.andReturn(new ArrayList<DtoStock>()).once();
EasyMock.replay(stockDao);
}
}
IStockDao needs to be mock in the test, so to get it I would like to use a #Produces method like this (in MockFactory class given to cdi unit by #AdditionalClasses):
#Produces
#ApplicationScoped
public IStockDao getStockDao() {
return EasyMock.createMock(IStockDao.class);
}
When I run my unit test, the mock is good in unit test but I get this error:
java.lang.IllegalArgumentException: Not a mock:
org.jboss.weld.proxies.IStockDao$-1971870620$Proxy$_$$_WeldClientProxy
This one comes because CDI doesn't give an instance of EasyMock IStockDao but a proxified instance and EasyMock doesn't accept this in these methods (like reset method).
So I replace #ApplicationScoped in MockFactory by #Dependent which doesn't proxified the instance but I have a new problem:
This annotation give a new instance of mock at each injection point so I can use it because I have a mock in the unit test to mock method called in the tested class. And this instance of mock must be the same in the tested class (it's not the case with #Dependent).
How can I get the same instance in the unit test and the tested class ?
Thanks.
Needle is your friend for testing CDI.
http://needle.spree.de
public class ResultYearMServiceImplTest {
#Rule
public final NeedleRule needle = new NeedleRule();
#Inject
private IStockDao stockDao;
#ObjectUnderTest
private ResultYearMServiceImpl resultYearMService;
#Test
public void getResultList() {
EasyMock.reset(stockDao);
EasyMock.expect(stockDao.getListStocks()).andReturn(null).once()
.andReturn(new ArrayList<DtoStock>()).once();
EasyMock.replay(stockDao);
}
}
I was unit testing a CDI interceptor with easymock and had the same problem as you.
I would like to share the workaround I used.
It consists in producing the mocks in #Dependent scope. This way we can get over the CDI proxy problem with easymock.
import static org.easymock.EasyMock.createStrictControl;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
/**
* Mock producer. Beans are produced in Dependent scope to not be proxied.
*/
#ApplicationScoped
public class CdiMockProducerUnitTests {
/**
* Mock
*/
private final MyMockClass myMock;
/**
* Constructor creating mocks.
*/
public CdiMockProducerTestesUnitarios() {
myMock = createStrictControl().createMock(MyMockClass.class);
}
/**
* Produces mock in dependent scope.
*
* #return mock
*/
#Produces
#Dependent
public MyMockClass produceMock() {
return myMock;
}
}
The next version of CDI-Unit (2.1.1) adds support for EasyMock in the same way that Mockito is currently supported.