Integration testing with Mockito and Spring Boot
Within this post, I show you how to setup a Unit Test in Spring Boot performing HTTP requests with Mockito. In this case, we’re using Model
in our controller, but we could follow similar instructions for a REST controller case.
Sometimes testing web access with Spring Boot can be tricky. There are some different annotations for different scenarios, and the configuration is also one of the most struggling parts.
Maven configuration
First, you will need to include the corresponding dependency in your Spring Boot application. In my case and using Maven it will be something like this:
<project ...>
[...]
<dependencies>
[...]
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

This is the dependency provided by Spring Boot, containing Mockito in it. As usual, we want the dependency only for test purposes so we set the scope to test .
The test case
The scenario is a web page that is going to be shown when doing a specific request. We’re expecting some data in the model that later will be used and rendered in the HTML, in this case using Thymeleaf (irrelevant for the test).
We want to test the controller and view so we are going to mock the service to return some predefined data.
package es.macero.cqgame.controller;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import es.macero.cqgame.app.ApplicationTest;
import es.macero.cqgame.domain.stats.SonarStatsRow;
import es.macero.cqgame.service.SonarStatsService;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(classes = ApplicationTest.class)
public class SonarStatsControllerTest{
private static final String TEAM_ONE = "Team One";
private static final String JOHN_ONE = "John One";
private static final String TEAM_TWO = "Team Two";
private static final String JOHN_TWO = "John Two";
private static final int INFO = 50;
private static final int MINOR = 40;
private static final int MAJOR = 30;
private static final int CRITICAL = 20;
private static final int BLOCKER = 10;
private static final int TOTAL_PAID_DEBT = 100;
private static final int TOTAL_POINTS = 25;
@Autowired
private WebApplicationContext context;
@Autowired
SonarStatsService sonarStatsService;
private MockMvc mvc;
@Before
public void setUp() {
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
@Test
public void testGetUsers() throws Exception {
SonarStatsRow statsRow1 = new SonarStatsRow(JOHN_ONE, TEAM_ONE, TOTAL_POINTS, TOTAL_PAID_DEBT, BLOCKER,
CRITICAL, MAJOR, MINOR, INFO, new ArrayList<>());
SonarStatsRow statsRow2 = new SonarStatsRow(JOHN_TWO, TEAM_TWO, TOTAL_POINTS, TOTAL_PAID_DEBT, BLOCKER,
CRITICAL, MAJOR, MINOR, INFO, new ArrayList<>());
List<SonarStatsRow> expectedStatsRows = Arrays.asList(statsRow1, statsRow2);
Mockito.when(sonarStatsService.getSortedStatsPerUser()).thenReturn(expectedStatsRows);
this.mvc.perform(get("/legacykillers/users")).andExpect(status().isOk()).andExpect(view().name("sonarstats"))
.andExpect(model().attribute("stats", expectedStatsRows));
}
}
Pay attention here to the highlighted lines since they are the key for this to work:
- @WebAppConfiguration is a specific annotation for integration tests (check Javadoc here). It tells the framework to load a WebApplicationContext .
- @SpringApplicationConfiguration annotation is used to pass a specific configuration for the test. In this case, I have created a separated class for that but an inner class could also be used. This is really important because if you don´t use a different configuration for testing all the default beans in your Spring Boot app are going to be loaded.
So now let’s see what this specific configuration for testing contains:
package es.macero.cqgame.app;
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import es.macero.cqgame.controller.SonarStatsController;
import es.macero.cqgame.service.SonarStatsService;
public class ApplicationTest {
@Bean
public SonarStatsService service() {
return Mockito.mock(SonarStatsService.class);
}
@Bean
public SonarStatsController controller(SonarStatsService service) {
return new SonarStatsController(service);
}
}
Easy. Just injecting the controller with a service mock. It’s in the Unit Test where we use Mockito to pass the expected values, this way we can reuse this configuration class.
Another valid option would be creating profiles, this way the configuration would be taken automagically. But this is another story…
Want to know more about proper Unit Testing and Integration Testing with Spring Boot?
- Check out The Complete Guide to REST (Controller) Tests in Spring Boot: Unit and Integration Tests
- Get my book to have a full understanding of how TDD works nicely with Spring Boot: Learn Microservices with Spring Boot
Comments