Integration Testing with Spring – Controllers II

Published by Gil Zilberfeld on

Gil Zilberfeld explains considerations for Spring controller integration tests
In this series we're taking at Spring and its features supporting testing in general, and specifically integration tests.
Dependency injectionConfigurationsNested configurationsOrganizing configurations
Primary beansAvoiding problemsProfilesMocking I - Lifecycle
Mocking II - ResetMocking III - MockBeanData I - @SQLData II - JDBC

Data III - JPA
Controllers IControllers IIConsumer tests

We’ve gone over what we needed from Spring to test a controller. And Spring is about to give it to us.

Here’s a controller:

public class ItemController {

	@Autowired ItemRepository itemRepository;

	@GetMapping(value ="/")
	public ResponseEntity<String> getAllItems() {
		Integer numberOfItems = itemRepository.findTotal();
		if (numberOfItems == 0)
			return new ResponseEntity<String>("Error", HttpStatus.SERVICE_UNAVAILABLE);
		else {

			String result =numberOfItems.toString() + " Items";
			return new ResponseEntity<String> (result , HttpStatus.OK);

	@PostMapping(value = "add/")
	public ResponseEntity<?> addItem(@RequestBody String itemName) {
		Item item = new Item(itemName, 1);
		return new ResponseEntity(HttpStatus.OK);

This controller has GET and POST APIs. It has some logic in it (although this is something we probably don’t want. We’d rather push that into our application logic layer). It also calls a Repository (it doesn’t matter which type, JDCB or JPA.) While we can just inject it into our integration tests, and call its methods directly, we’d rather call the APIs as a URI.

Remember: what we’ve wanted all along was a standalone server, that from its entry points onward, runs our code. That server can be called with an API and return an HTTP code and payload.

Spring comes with a handy class called MvcTestController. It is basically a web server that lives for the duration of the integration test run. It is limited to exposing only the API of the controllers-under-test, so not all the application loads. Our wish has comes true, with a bonus.

In order to create it, we have a couple of ways. The first one is to use the WebApplicationContext, which Spring creates and injects automatically.

@Autowired WebApplicationContext wac;
private MockMvc mockMvc;

public void setup() {
	mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)

The mockMvc is created for each integration test. But even that maybe too much, so the good people of Spring thought of us lazy bastards, and gave us an annotation for our integration test class:

@ContextConfiguration(classes= {TestControllerConfiguration.class})
public class ControllerTests {

	@Autowired MockMvc mockMvc;

@AutoConfigureMockMvc basically injects mockMvc with less coding. So what can this thing do?

public void whenNoItemsAvailable_thenGetReturnsAnErrorCode() throws Exception {

TestMockMvc comes packed with a fluent interface to do all the HTTP operations (here we do a HTTP GET) and asserts if we got the right status. It can do a lot more though: We can pass in headers, request bodies, authorization data. And we can return all kind of statuses and check the response body. Here’s another integration test:

public void whenItemsAreAdded_thenGetReturnsNumberOfItems() throws Exception {



	MvcResult result =  mockMvc.perform(get("/items/"))

	assertEquals("2 Items",result.getResponse().getContentAsString());

Here’s the JSON serialization method, used to wrap the strings:

private String asJsonString(Object obj) {
		try {
			return new ObjectMapper().writeValueAsString(obj);
		} catch (Exception e) {
			throw new RuntimeException(e);

As you can see, the TestMockMvc allows us to operate and check the controller as if it was called from the outside. While you might be tempted to write all your tests like this, remember that the longer the test path, the integration test can fail for many reasons.

There’s another downside: integration tests tend to get longer and longer, and therefore hard to read and maintain. Which makes it very important to extract those repeated calls into methods and leave the integration test like this:

public void api_whenItemsAreAdded_thenGetReturnsNumberOfItems() throws Exception {
	assertEquals("2 Items",getTotal());

Much better, right? That’s how we like our integration tests.

So that’s the TestMockMvc. What I’d like next is to mock a full API call. Can I do that? Of course I can. Next time.

Categories: Uncategorized


Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *