waffel’s Weblog

Februar 27, 2013

testing REST interface with Spring 3.2 and a session scoped bean

Filed under: java,spring — Thomas Wabner @ 8:00 nachmittags
Tags: , , , , , , , , ,

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:

  1. Get a list of all customers and remember the count
  2. Create a new customer
  3. Check, that the ID of the returning Customer was updated
  4. Get again a list of all customers
  5. 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.

The Rubric Theme. Erstelle eine kostenlose Website oder einen kostenlosen Blog – auf WordPress.com!.

Folgen

Erhalte jeden neuen Beitrag in deinen Posteingang.

Schließe dich 28 Followern an