SpringBoot Testing with Request Controllers

Learn how Spring Boot, a famous framework for developing microservices supports testing request controllers.

SpringBoot is a famous framework for developing microservices these days. But there are few things you need to know when you are testing things written with SpringBoot. For instance, your microservice might have several request controllers catering to different inputs and outputs.

So with this short read, I'm going to focus on how to test Spring controllers with Junit. When testing your request controllers it can be sometimes referred to as an integration test as well. Why I’m saying this is because sometimes we tend to test the actual output for a given input to the controller and evaluate and assert the status returned from the controller. So there is always a debate going on if we are writing unit tests or integration tests. But both of those adds value to your core business components. Because you are testing things :)

For testing you need to have the following dependency in your project

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

With spring controllers you have the MockMvc which is a SpringBoot test tool that allows testing controllers without a need of the actual server. It uses the loaded application context along with the mocking environment. So that you do not have to bother about injecting all other dependencies.

The very first thing you have to do is use the annotation to direct that the test is using Junit4,

@RunWith(SpringRunner.class), this is an instruction to say that below code is using Junit4 for test executions.

Then comes the all in one handy annotation for SpringBoot test, @SpringBootTest. It will make you a mock environment with all the required settings. In a way, this is kind of an alternative for good old spring annotation@ContextConfiguration - where you can provide all the required classes for loading the application context. For instance, with @ContextConfiguration you might have to provide all the context classes you need to boot up the application,

@ContextConfiguration(classes = MyAutoWire.class) But with your @SpringBootTest annotation this is no longer required. But sometimes there can be situations in your application that some tests need to use different property settings than in the default properties provided with the application. Let’s say some cache setting, in your application, it might be using the actual production setting. But in your test code you do not need to use those values, but only a different value (maybe a small cache size). For these cases, you can use below annotation to provide those custom properties to your test code executions.

@TestPropertySource(properties = {
    "com.my.company.cache.size=12"
})

Next, you need to tell that your MockMvc object should get auto-configured, that is why you need

@AutoConfigureMockMvc. But there are ways to manually configure the MockMvc instance you are going to use in the test code. But it is more lines of code. So why use that instead of a pretty cool one-time annotation.

Finally what you have to do is posting the request to mock environment and assert those of returned results.

MockHttpServletResponse response = mvc.perform(get(String.format("/user/information/%s", userId))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andReturn().getResponse();

Now we are using the MockMvc instance we received and calling it’s perform() method.

So the above code is simply calling the /user/information/34 for the request. And it is extracting the response returned to a variable where we can then use for assertions.

Assert.assertEquals(HttpStatus.OK.value(), response.getStatus());
 
String json = response.getContentAsString();
 
JSONObject reply = new JSONObject(json);
 
Assert.assertNotNull(reply);
 
Assert.assertEquals("userId", reply.getString("userId"));
 
Assert.assertEquals("Admin", reply.getString("userType"));
 
Assert.assertEquals("Colombo", reply.getString("address"));

Since the response is a JSON , we are then extracting it and creating an instance of JSONObject to further testing.

To make it a mock reply, just like in other SpringBoot service tests, we will use some mockito syntaxes ,

given(userService.getUserInformation(userId)).willReturn(userOutPut);

Full test case :

import org.json.JSONObject;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.BDDMockito.given;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
import com.mycompany.service.UserService;
 
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {
 
    @Autowired
    private MockMvc mvc;
 
    @MockBean
    private UserService userService;
 
    @Test
    public void testGetUserWhenDataFound()
            throws Exception {
 
        String userId= "34";
        User userOutPut = new User();
        userOutPut.setUserId(userId);
        userOutPut.setUserType("Admin");
        userOutPut.setAddress("Colombo");
               
        given(userService.getUserDetails(userId)).willReturn(userOutPut );
       
        MockHttpServletResponse response = mvc.perform(get(String.format("/user/information/%s", userId))
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk()).andReturn().getResponse();
        Assert.assertEquals(HttpStatus.OK.value(), response.getStatus());
        String json = response.getContentAsString();
        JSONObject reply = new JSONObject(json);
        Assert.assertNotNull(reply);
        Assert.assertEquals("userId", reply.getString("userId"));
        Assert.assertEquals("Admin", reply.getString("userType"));
        Assert.assertEquals("Colombo", reply.getString("address"));
 
    }
}