Unit Testing Spring REST Controllers Via Mock MVC
I would start off with a disclaimer that REST based controllers are better tested using integration tests or using a more suitable mocking framework like wiremock.
However, assuming that you may still want to use Mock MVC in your junit tests, we can do something like the following:
1.) Let us say we want to test a (super simplifed) controller that looks something like:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/fizzbuzz/v1/*")
public class MyController {
private SomeComponent bean;
@Autowired
public void setBean(SomeComponent bean) {
this.bean = bean;
}
@RequestMapping(value="/action" , method = {RequestMethod.POST})
public ResponseEntity<?> execute()
{
bean.setState("CREATED");
return ResponseEntity.ok().build();
}
}
The “SomeComponent” in the above example is managed by Spring (using the “@Component” annotation).
2.) The gradle dependencies that we will require are:
// Use JUnit test framework
testImplementation 'junit:junit:' + "${junitVersion}"
testCompile group: 'org.mockito', name: 'mockito-core', version: "${mockitoCoreVersion}"
testCompile group: 'org.springframework', name: 'spring-test', version: "${springTestVersion}"
testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: "${springBootStarterTestVersion}"
3.) The test class will look something like:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
public class LCMControllerTest {
@InjectMocks
private MyController controllerUnderTest;
@Spy
private SomeComponent bean;
private MockMvc mockMvc;
@Before
public void startService() throws Exception{
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(controllerUnderTest).build();
}
@Test
public void testExecute()throws Exception{
mockMvc.perform(post("http://<base_url>/fizzbuzz/v1/action"))
.andDo(print())
.andExpect(status().isOk());
verify(bean,atLeastOnce()).setState("CREATED");
}
}
Obviously, this is a very simplified example and essentially illustrates the bare bones of mock mvc usage in junit tests. This however can be used to build unit tests around REST controllers.