Testing a Spring RESTful Web Service

May 10, 2014

Untested code is legacy code!

Unit testing a Spring RESTful Web Service is quite easy, however this is not enough to ensure that your exposed logic actually works. In the past we had to deploy our application somewhere to run an integration test. Integration tests have a few big drawbacks, they are slow and hard to maintain. Luckily Spring has provided us with some kick-ass features to easily test a RESTful Service.

Pirate PoC

Code examples are available on github.

Within the next code block, you will find a straightforward controller, called ‘PirateController’. This controller exposes two methods: ‘hi’ and ‘myShip’.

Method ‘hi’ takes a forename as a parameter and returns a customised pirate greeting as plain text.
Method ‘myShip’ returns a new instance of a pirate ship as JSON.

I am using the @RestController annotation, introduced in Spring 4, so there is no need to annotate the methods with @ResponseBody.

@RestController
public class PirateController {

    @RequestMapping(
            value = "/hi",
            produces = MediaType.TEXT_PLAIN_VALUE)
    public String hi(@RequestBody final String name) {
        return "Ahoy, " + name;
    }

    @RequestMapping(
            value = "/myShip",
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Ship myShip() {
        return Ship.of(
                "Royal Fortune", "Frigate", "Bartholomew Roberts");
    }
}
Now how do we test this implementation?

Instead of testing the code directly, we want to test how clients are going to communicate to our service. This means that we need to interact, using the RESTful url’s that are mapped to the service, through the magical powers of Spring MVC.
To do this we have to create a MockMvc object which loads and initialises our service.

    public class PirateControllerTest {
        MockMvc mockMvc;

        @Before
        public void setUp() throws Exception {
            mockMvc = standaloneSetup(new PirateController()).build();
        }

Now it's possible to perform ‘builder style’ queries on this objects to mimic calls to the ‘PirateController’. As a first test, trigger the ‘hi’ method by performing a get to ‘/hi’ and add ‘Jack’ as payload.

    @Test
    public void sayHi() throws Exception {
        mockMvc.perform(get("/hi").content("Jack"))

The result of the previous statement will return an instance of ResultActions, which contains a lot of ‘builder style’ asserts.

      mockMvc.perform(get("/hi").content("Jack"))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.TEXT_PLAIN))
                .andExpect(content().string("Ahoy, Jack"));

The first ‘andExpect’ method checks that the status code of the web call is ‘200 OK’. The next ‘andExpect’ method is chained to the previous method and validates that the content type is ‘text/plain’. Within the last ‘andExpect’ method, we assure that the content is the expected greeting.
Note that you can keep on adding more assert statements, there are a lot of things to test!

We can apply the same logic for the second method, and check that the content is the expected JSON object.

 @Test
    public void myShip() throws Exception {
        mockMvc.perform(get("/myShip"))
                .andExpect(status().isOk())
                .andExpect(content()
                    .contentType(MediaType.APPLICATION_JSON))
                .andExpect(content()
                    .string("{\"name\":\"Royal Fortune\",\"type\":\"Frigate\",\"captain\":\"Bartholomew Roberts\"}"));
    }

The execution time of this test class is below one seconde, which is lightning fast compared to our old integration tests!

Conclusion

Testing a Spring RESTful Web Service is as easy and fast as a unit test, so there is really no excuse not to test it!

References

Jeroen Bellen
Alken
Dissident blogger!