Integration Testing in Spring

1. Overview

Integration testing plays an important role in the application development cycle by verifying end-to-end behavior of the system.

In this article we will see how we can leverage the Spring MVC test framework in order to write and run integration tests that test controllers without explicitly starting a Servlet container.

2. Preparation

The following Maven dependencies are needed for running integration tests as described in this article. First and foremost the latest JUnit and Spring test dependencies:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>4.3.2.RELEASE</version>
    <scope>test</scope>
</dependency>

For effective asserting of results, we’re going to also use Hamcrest and JSON path:

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-library</artifactId>
    <version>1.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.2.0</version>
    <scope>test</scope>
</dependency>

3. Spring MVC Test Configuration

Let’s now introduce how to configure and run the Spring enabled tests.

3.1. Enable Spring in Tests

First, any Spring enabled test will run with the help of @RunWith(SpringJUnit4ClassRunner.class); the runner is essentially the entry-point to start using the Spring Test framework.

We also need the @ContextConfiguration annotations to load the context configuration and bootstrap the context that the test will use.

Let’s have a look:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ApplicationConfig.class })
@WebAppConfiguration
public class GreetControllerIntegrationTest {
    ....
}

Notice how, in @ContextConfiguration, we provided the ApplicationConfig.class config class which loads the configuration we need for this particular test.

We used a Java configuration class here to specify the context configuration; similarly we can use the XML based configuration:

@ContextConfiguration(locations={""})

Finally – the test is also annotated with @WebAppConfiguration – which will load the web application context.

By default, it looks for the root web application at default path src/main/webapp; the location can be overridden by passing value argument as:

@WebAppConfiguration(value = "")

3.2. The WebApplicationContext Object

WebApplicationContext (wac) provides the web application configuration. It loads all the application beans and controllers into the context.

We’ll now be able to wire the web application context right into the test:

@Autowired
private WebApplicationContext wac;

3.3. Mocking Web Context Beans

MockMvc provides support for Spring MVC testing. It encapsulates all web application beans and make them available for testing.

Let’s see how to use it:

private MockMvc mockMvc;
@Before
public void setup() throws Exception {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}

We need to initialize the mockMvc object in the @Before annotated method, so that we don’t need to initialize it inside every test.

3.4. Verify Test Configuration

For our tutorial here, let’s actually verify that we’re loading the WebApplicationContext object (wac) properly. We’ll also verify that the right servletContext is being attached:

@Test
public void givenWac_whenServletContext_thenItProvidesGreetController() {
    ServletContext servletContext = wac.getServletContext();

    Assert.assertNotNull(servletContext);
    Assert.assertTrue(servletContext instanceof MockServletContext);
    Assert.assertNotNull(wac.getBean("greetController"));
}

Notice that we’re also checking that we a GreetController.java bean exists in the web context – which ensures that spring beans are loaded properly.

At this point, the setup of integration test, is done. Let us see how we can test resource methods using the MockMvc object.

4. Writing Integration Tests

In this section we’ll go over the basic operation available through the test framework.

We’ll show how to send requests with path variables and parameters. Also, we’ll follow with the few examples that show how to assert that the proper view name is resolved, or that the response body is as expected.

The following snippets use static imports from MockMvcRequestBuilders or MockMvcResultMatchers classes.

4.1. Verify View Name

Let’s invoke the /homePage endpoint from our test as:

http://localhost:8080/spring-mvc-test/

or

http://localhost:8080/spring-mvc-test/homePage

Code Snippet:

@Test
public void givenHomePageURI_whenMockMVC_thenReturnsIndexJSPViewName() {
    this.mockMvc.perform(get("/homePage")).andDo(print())

      .andExpect(view().name("index"));
}

Let’s break that down:

  • perform() method will call a get request method which returns the ResultActions. Using this result we can have assertion expectations on response like content, HTTP status, header, etc

  • andDo(print()) will print the request and response. This is helpful to get detailed view in case of error

  • andExpect() will expect the provided argument. In our case we are expecting “index” to be returned via MockMvcResultMatchers.view()

4.2. Verify Response Body

We will invoke /greet endpoint from our test as:

http://localhost:8080/spring-mvc-test/greet

Expected Output:

{
    "id": 1,
    "message": "Hello World!!!"
}

Code Snippet:

@Test
public void givenGreetURI_whenMockMVC_thenVerifyResponse() {
    MvcResult mvcResult = this.mockMvc.perform(get("/greet"))
      .andDo(print()).andExpect(status().isOk())
      .andExpect(jsonPath("$.message").value("Hello World!!!"))
      .andReturn();

    Assert.assertEquals("application/json;charset=UTF-8",
      mvcResult.getResponse().getContentType());
}

Let’s see exactly what’s going on:

  • andExpect(MockMvcResultMatchers.status().isOk()) will verify that response http status is Ok i.e. 200. This ensures that request was successfully executed

  • andExpect(MockMvcResultMatchers.jsonPath(“$.message”).value(“Hello World!!!”)) will verify that response content matches with the argument “Hello World!!!“. Here we used jsonPath which extracts response content and provide the requested value

  • andReturn() will return the MvcResult object which is used, when we have to verify something which is not achievable by library. You can see we have added assertEquals to match the content type of response that is extracted from MvcResult object

4.3. Send GET Request with Path Variable

We will invoke /greetWithPathVariable/{name} endpoint from our test as:

http://localhost:8080/spring-mvc-test/greetWithPathVariable/John

Expected Output:

{
    "id": 1,
    "message": "Hello World John!!!"
}

Code Snippet:

@Test
public void givenGreetURIWithPathVariable_whenMockMVC_thenResponseOK() {
    this.mockMvc
      .perform(get("/greetWithPathVariable/{name}", "John"))
      .andDo(print()).andExpect(status().isOk())

      .andExpect(content().contentType("application/json;charset=UTF-8"))
      .andExpect(jsonPath("$.message").value("Hello World John!!!"));
}

MockMvcRequestBuilders.get(“/greetWithPathVariable/{name}”, “John”) will send request as “/greetWithPathVariable/John“.

This becomes easier with respect to readability and knowing what are the parameters which are dynamically set in URL. This method doesn’t restricts to pass number of path parameters.

4.4. Send GET Request with Query Parameters

We will invoke /greetWithQueryVariable?name={name} endpoint from our test as:

http://localhost:8080/spring-mvc-test
  /greetWithQueryVariable?name=John%20Doe

Expected Output:

{
    "id": 1,
    "message": "Hello World John Doe!!!"
}

Code Snippet:

@Test
public void givenGreetURIWithQueryParameter_whenMockMVC_thenResponseOK() {
    this.mockMvc.perform(get("/greetWithQueryVariable")
      .param("name", "John Doe")).andDo(print()).andExpect(status().isOk())
      .andExpect(content().contentType("application/json;charset=UTF-8"))
      .andExpect(jsonPath("$.message").value("Hello World John Doe!!!"));
}

param(“name”, “John Doe”) will append the query parameter in the GET request. It is similar to “/greetWithQueryVariable?name=John%20Doe“.

The query parameter can also be implemented using URI template style:

this.mockMvc.perform(
  get("/greetWithQueryVariable?name={name}", "John Doe"));

4.5. Send POST Request

We will invoke /greetWithPost endpoint from our test as:

http://localhost:8080/spring-mvc-test/greetWithPost

Expected Output:

{
    "id": 1,
    "message": "Hello World!!!"
}

Code Snippet:

@Test
public void givenGreetURIWithPost_whenMockMVC_thenVerifyResponse() {
    this.mockMvc.perform(post("/greetWithPost")).andDo(print())
      .andExpect(status().isOk()).andExpect(content()
      .contentType("application/json;charset=UTF-8"))
      .andExpect(jsonPath("$.message").value("Hello World!!!"));
}

MockMvcRequestBuilders.post(“/greetWithPost”) will send the post request. Path variables and Query Parameters can be set in similar way we looked earlier, whereas Form Data can be set via param() method only similar to Query Parameter as:

http://localhost:8080/spring-mvc-test/greetWithPostAndFormData

Form Data:

id=1;name=John%20Doe

Expected Output:

{
    "id": 1,
    "message": "Hello World John Doe!!!"
}

Code Snippet:

@Test
public void givenGreetURIWithPostAndFormData_whenMockMVC_thenResponseOK() {
    this.mockMvc.perform(post("/greetWithPostAndFormData").param("id", "1")
      .param("name", "John Doe")).andDo(print()).andExpect(status().isOk())

      .andExpect(content().contentType("application/json;charset=UTF-8"))
      .andExpect(jsonPath("$.message").value("Hello World John Doe!!!"))
      .andExpect(jsonPath("$.id").value(1));
}

In above code snippet we have added 2 parameters id as “1” and name as “John Doe”.

5. Conclusion

In this quick tutorial we implemented a few simple Spring enabled integration tests.

We also looked at the WebApplicationContext and MockMVC object creation which played important role in calling the endpoints of the application.

Looking further we covered how we can send GET and POST request with variations of parameter passing and how to verify the HTTP response status, header and content.

Finally, the implementation of all these examples and code snippets is available in https://github.com/eugenp/tutorials/tree/master/spring-mvc-java.

Leave a Reply

Your email address will not be published.