How to Unit Test Spring MVC Controller


Let’s assume we have the following controller that needs to be tested:-

@RequestMapping(value = "/person")
public class PersonController {

    private PersonService personService;

    public PersonController(PersonService personService) {
        this.personService = personService;

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String getPerson(@PathVariable Long id, Model model) {
        model.addAttribute("personData", personService.getPerson(id));
        return "personPage";

SOLUTION 1: “Works but It Won’t Get You the Promotion”

This working solution relies on:-

  • JUnit – General unit test framework.
  • Mockito – To mock PersonService.
public class PersonControllerTest {

    public void testGetPerson() {

        PersonService personService = mock(PersonService.class);

        when(personService.getPerson(1L)).thenReturn(new Person(1L, "Chuck"));

        PersonController controller = new PersonController(personService);

        Model model = new ExtendedModelMap();

        String view = controller.getPerson(1L, model);

        assertEquals("View name", "personPage", view);

        Person actualPerson = (Person) model.asMap().get("personData");

        assertEquals("matching ID", Long.valueOf(1), actualPerson.getId());
        assertEquals("matching Name", "Chuck", actualPerson.getName());

While this solution works, but it has a few problems. This test case strictly tests the actual controller API, but it completely disregards the URI and request method (GET, POST, PUT, DELETE, etc). For instance:-

  • What if the URI has a typo ( /persn/1 ) or it is not properly constructed ( /person/donkey-kong )?
  • What if the request method should be a POST but we accidentally used a GET?

SOLUTION 2: “A Mind Blowing Solution that Still Won’t Get You the Promotion but You Feel So Invincible That You Feel Compelled to Flip a Table Over”

This better solution relies on:-

  • JUnit – General unit test framework.
  • Mockito – To mock PersonService.
  • Spring MVC Test Framework – To properly test the controller.
  • Hamcrest – To clean way to assert the actual result is correct.
// This XML configuration basically enable component scanning.
// You could have used @Configuration and @ComponentScan to do the same thing.
public class PersonControllerTest {

    private PersonService personService;

    private PersonController personController;

    private MockMvc mockMvc;

    public void setup() {
        mockMvc = MockMvcBuilders.standaloneSetup(personController).build();

    public void testGetPerson() throws Exception {
        when(personService.getPerson(1L)).thenReturn(new Person(1L, "Chuck"));

        mockMvc.perform(get("/person/{id}", 1L))
                                             allOf(hasProperty("id", is(1L)),
                                                   hasProperty("name", is("Chuck")))));

Basically object that is annotated with @Mock will get injected into object that is annotated with InjectMocks. Then, we rely on Spring MVC Test Framework’s MockMvc to test our controller in a very clean and detailed fashion.

Okay, you may flip a table over now…

By the way…

You need at least Spring 3.2 to use MockMvc.


If you are using an older Mockito version, you will get this error:-

org.mockito.exceptions.base.MockitoException: Field 'personController' annotated with @InjectMocks is null.
Please make sure the instance is created *before* MockitoAnnotations.initMocks();
Example of correct usage:
   class SomeTest {
      @InjectMocks private Foo foo = new Foo();

      @Before public void setUp() {

Upgrading Mockito to the latest version will fix this error:-