There are some nice articles around the „spring 3.2 testing capabilities“ like the spring documentation itself, this blog and this blog.
I wanted to not only test one request/response action against my REST interface. I wanted to simulate and test more of a conversation as it typical happens in the UI.
Following REST interface I wanted to create and test:
@RequestMapping("/customer") public interface RESTCustomer { @RequestMapping( method = RequestMethod.POST) @ResponseBody Customer create(@RequestParam("firstname") final String firstname, @RequestParam("lastname") final String lastname); @RequestMapping( method = RequestMethod.DELETE) @ResponseStatus(HttpStatus.OK) void delete(@RequestParam("id") final String... customerIds); @RequestMapping( produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET) @ResponseBody Collection<Customer> getAll(); @RequestMapping( value = "/update", method = RequestMethod.POST) @ResponseBody Customer update(@RequestParam("id") final String id, @RequestParam("firstname") final String firstname); }
Following test steps I had in mind, to test the create method:
- Get a list of all customers and remember the count
- Create a new customer
- Check, that the ID of the returning Customer was updated
- Get again a list of all customers
- Check the size of the customer list before and after creation … they should differ between one entry/li>
The new Spring Framework version 3.2 introduced some nice feature to do REST testing with minimal effort.
To test such conversion, you need beside the WebApplicationContext a MockHttpSession which has to be used between different mockMvc calls, on one test case.
The following base test structure is required:
@RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration( classes = { MyConfig.class, }) public class RESTCustomerTest { @Autowired private WebApplicationContext wac; @Autowired MockHttpSession session; MockMvc mockMvc; ObjectMapper jsonObjectMapper; @Before public void setup() { mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); jsonObjectMapper = new ObjectMapper(); } @Test public void testCreate() throws Exception { .... } }
Let assume, there is an implementation of the RESTCustomer service interface like this:
@Component public class CustomerResource implements RESTCustomer { private final PlatformService service; @Autowired public CustomerResource(final PlatformService service) { this.service = service; } @Override public Customer create(final String firstname, final String lastname) { ... return service.createCustomer(firstname, lastname); } @Override public Collection<Customer> getAll() { ... return service.getAllCustomers(); } }
Assume also, that the autowired PlatformService is a Session scoped bean (somewhere configured inside the spring configuration).
Now our testing method can be like this:
int countCustomers() throws Exception { final MvcResult mvcResult = mockMvc.perform(get("/customer").session(session).accept(MediaType.APPLICATION_JSON)) .andReturn(); final Collection<Customer> customers = getRawObjects(mvcResult); return customers.size(); } Collection<Customer> getRawObjects(final MvcResult mvcResult) throws Exception { return jsonObjectMapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<Collection<Customer>() { }); } Customer getRawObject(final MvcResult mvcResult) throws Exception { return jsonObjectMapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<Customer>() { }); } @Test public void testCreate() throws Exception { // get default customer list and count final int originalCustomerSize = countCustomers(); // create new customer final MvcResult mvcResult = mockMvc .perform( post("/customer/?firstname={firstname}&lastname={lastname}","testFirstname", "testLastName").session(session).accept( MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andReturn(); final Customer customerObject = getRawObject(mvcResult); assertNotNull("id of new customer should not empty", customerObject.get("id")); assertEquals("newly firstname should match", "testFirstname", customerObject.get("firstname"); // again get list of all customers and check, if one more is available assertEquals("after new customer was created, the size should be one more", originalCustomerSize + 1, countCustomers()); }
To get such conversation to work, you need to pass the session with .session(session)
between the mockMvc
calls. Else, every new mockMvc call creates a new session and your test fail.
REMEMBER: The trick is to pass the autowired MockHttpSession between the mock MVC requests.