Testing Spring MVC controllers with Spock

23/6/2014

Spring 3.2 has added a very nice testing abstraction for MVC controllers which works well with the Spock framework.

Say you have a controller like this:

@Controller
@RequestMapping("/some")
public class SomeController {

    private SomeService someService;

    @RequestMapping(method = RequestMethod.POST,
            produces = MediaType.APPLICATION_JSON_VALUE,
            consumes = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public BulkMessage someMethod(@RequestBody final BulkMessage request) {
        final String result = someService.someMethod(request.getUrl());
        return new BulkMessage(result);
    }
}

Then you can test your controller using Spock and MockMvc like this:

import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import spock.lang.Specification

import static org.hamcrest.Matchers.*
import static org.springframework.http.MediaType.APPLICATION_JSON
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*

class SomeControllerTest extends Specification {

    def someService = Mock(SomeService)

    def underTest = new SomeController(someService: someService)

    def mockMvc = MockMvcBuilders.standaloneSetup(underTest).build()

    def 'someMethod() forwards to service and returns result as a JSON'() {
        when:
        def response = mockMvc.perform(post('/some')
                .contentType(APPLICATION_JSON)
                .content('{"url": "/some/value"}')
        )

        then:
        1 * someService.someMethod('/some/value') >> 'some/other/value'

        response
                .andExpect(status().isOk())
                .andExpect(content().contentType(APPLICATION_JSON))
                .andExpect(jsonPath('$.url', is('some/other/value')))
    }
}

This allows you to test using Spock's mocking and without pulling up a Spring context, which is really quite fantastic in comparison what you had to do before.

To be able to use the JSON matchers from this example, you also need these dependencies:

    <dependency>
        <groupId>com.jayway.jsonpath</groupId>
        <artifactId>json-path</artifactId>
        <version>0.8.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.jayway.jsonpath</groupId>
        <artifactId>json-path-assert</artifactId>
        <version>0.8.1</version>
        <scope>test</scope>
    </dependency>

Comments