When Spring Framework 3.2 was released, spring-test-mvc was officially included in this release under the name Spring MVC Test Framework. Although many things remained the same, there are some changes that might cause confusion when we are migrating our applications from Spring Framework 3.1 to 3.2.
This blog entry was written to help us to migrate our integration integration tests to Spring Framework 3.2. It describes how we can get the required dependencies with Maven and update our existing integration tests to use the Spring MVC Test Framework. As an example, we will migrate the example application of my previous blog entry from Spring Framework 3.1 to 3.2.
Getting the Required Dependencies
When our application uses Spring Framework 3.2, we should not use the standalone spring-test-mvc project in our integration tests. Instead, we should use the spring-test module of Spring Framework 3.2. We can get the required dependencies by following these steps:
- Declare the Hamcrest (version 1.3) dependency in our pom.xml file. This ensures that we can use the newest Hamcrest matchers in our tests.
- Add the JUnit dependency (version 4.10) in our pom.xml file and exclude the hamcrest-core dependency.
- Declare the Spring test (version 3.2.0.RELEASE) dependency in our pom.xml file.
- Declare the Spring Test DBUnit (version 1.0.0) dependency in our pom.xml file.
- Declare DBUnit (version 2.4.8) dependency in the pom.xml file.
The dependency declarations of our testing dependencies looks as follows:
1<dependency>
2 <groupId>org.hamcrest</groupId>
3 <artifactId>hamcrest-all</artifactId>
4 <version>1.3</version>
5 <scope>test</scope>
6</dependency>
7<dependency>
8 <groupId>junit</groupId>
9 <artifactId>junit</artifactId>
10 <version>4.10</version>
11 <scope>test</scope>
12 <exclusions>
13 <exclusion>
14 <artifactId>hamcrest-core</artifactId>
15 <groupId>org.hamcrest</groupId>
16 </exclusion>
17 </exclusions>
18</dependency>
19<dependency>
20 <groupId>org.springframework</groupId>
21 <artifactId>spring-test</artifactId>
22 <version>3.2.0.RELEASE</version>
23 <scope>test</scope>
24</dependency>
25<dependency>
26 <groupId>com.github.springtestdbunit</groupId>
27 <artifactId>spring-test-dbunit</artifactId>
28 <version>1.0.0</version>
29 <scope>test</scope>
30</dependency>
31<dependency>
32 <groupId>org.dbunit</groupId>
33 <artifactId>dbunit</artifactId>
34 <version>2.4.8</version>
35 <scope>test</scope>
36</dependency>
Migrating Our Integration Tests
This section describes how we can migrate our integration tests from spring-test-mvc to Spring MVC Test Framework. We will start by taking a look at the changes made to package and method names, learn to configure our integration tests by using the Spring MVC Test framework and take look at changes which we have to make to our existing integration tests.
Changes in Package and Method Names
These change in package and method names are described in the following:
- The package org.springframework.test.web.server has a new name. It is now called org.springframework.test.web.servlet.
- The body() method of the MockHttpServletRequestBuilder class has been renamed to content().
- The mimeType() method of the ContentResultMatchers class has been renamed to contentType().
As we can see, the changes are cosmetic and should not cause us a lot of trouble. Lets move on and find out how we can configure our integration tests when our application uses Spring Framework 3.2.
Note: If you notice that something is missing, please a leave comment and I will update this list.
Configuration Changes
The configuration of Spring MVC Test Framework is a bit different than the configuration of the spring-test-mvc project. These differences are addressed in this subsection.
The @WebAppConfiguration annotation can be used on a class level to declare that the application context used by that class is an instance of WebApplicationContext. The created WebApplicationContext can be injected to the test class by adding a WebApplicationContext field to the test class and annotating that field with the @Resource annotation. This means that we can get rid of the application context loader classes (GenericWebContextLoader and WebContextLoader) which we used to load and inject the WebApplicationContext into our test classes.
The Spring MVC Test Framework supports two configuration methods:
- A new MockMvc object can be created by using the web application context that is injected to the test class.
- Standalone setup where the tested controller and its dependencies are configured manually.
The configuration methods which used either an application context configuration class or configuration file are no longer supported. The reference documentation of Spring Framework provides more information about the configuration of the Spring MVC Test Framework.
We can configure our integration tests by following these steps:
- Annotate the test class with the @RunWith annotation. This annotation is used to declare the test runner which is used to run our integration test. The correct test runner is the SpringJUnit4TestRunner.
- Annotate the test class with the @ContextConfiguration annotation and set the used application context configuration class or file.
- Annotate the class with the @WebAppConfiguration annotation.
- Use the @TestExecutionListener annotation to add test execution listeners required by the Spring Test DBUnit. If you are not familiar with the Spring Test DBUnit, you might want to check out the second part of my spring-test-mvc tutorial. Naturally, we can skip this phase if our tests are not using database.
- Use the @DatabaseSetup annotation to specify the DBUnit dataset file that is used to initialize our database to a known state before tests are run. We can skip this test if our tests are not using database.
- Add a FilterChainProxy field to our test class and annotate it with the @Resource annotation. This field contains a reference to the Spring security filter chain. If we are not using Spring Security, we can skip this step.
- Add a WebApplicationContext field to our test class and annotate it with the @Resource annotation. This field contains a reference to the used web application context.
- Add a MockMvc field to our test class. This field contains a reference to the MockMvc object that is used in our integration tests.
- Create a public setUp() method and annotate this method with the @Before annotation. This method creates a new MockMvc object by using the static webAppContextSetup() method of the MockMvcBuilders class and adds the Spring Security filter chain into our test (this is naturally not required if our test test does not use Spring Security)
The source code of our integration test skeleton class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import org.junit.Before;
4import org.junit.runner.RunWith;
5import org.springframework.security.web.FilterChainProxy;
6import org.springframework.test.context.ContextConfiguration;
7import org.springframework.test.context.TestExecutionListeners;
8import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
9import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
10import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
11import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
12import org.springframework.test.context.web.WebAppConfiguration;
13import org.springframework.test.web.servlet.MockMvc;
14import org.springframework.test.web.servlet.setup.MockMvcBuilders;
15import org.springframework.web.context.WebApplicationContext;
16
17import javax.annotation.Resource;
18
19@RunWith(SpringJUnit4ClassRunner.class)
20@ContextConfiguration(classes = {ExampleApplicationContext.class})
21//@ContextConfiguration(locations = {"classpath:exampleApplicationContext.xml"})
22@WebAppConfiguration
23@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
24 DirtiesContextTestExecutionListener.class,
25 TransactionalTestExecutionListener.class,
26 DbUnitTestExecutionListener.class })
27@DatabaseSetup("toDoData.xml")
28public class ITTest {
29
30 @Resource
31 private FilterChainProxy springSecurityFilterChain;
32
33 @Resource
34 private WebApplicationContext webApplicationContext;
35
36 private MockMvc mockMvc;
37
38 @Before
39 public void setUp() {
40 mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
41 .addFilter(springSecurityFilterChain)
42 .build();
43 }
44
45 //Add tests here
46}
Writing an Integration Test
This subsection describes the common testing utilities which are used in our integration tests and demonstrates how we can write an integration test by using the Spring MVC Test Framework.
Common Testing Utilities
Our integration tests are using four testing utilities that are described in the following:
- The TodoTestUtil class is used in the unit and integration tests or our todo application.
- The IntegrationTestUtil class is used only in our integration tests.
- The DBUnit dataset is used to initialize our database into a known state before our tests are run.
- The SecurityRequestPostProcessor class is used to create security context for Spring Security.
Lets move on and take a closer look at these testing utilities.
The TodoTestUtil class has one static method that is used in our integration tests. The TodoDTO createDTO(Long id, String description, String title) method is used to create new TodoDTO objects. The source code of the TodoTestUtil class looks as follows:
1import net.petrikainulainen.spring.testmvc.todo.dto.TodoDTO;
2
3public class TodoTestUtil {
4
5 public static TodoDTO createDTO(Long id, String description, String title) {
6 TodoDTO dto = new TodoDTO();
7
8 dto.setId(id);
9 dto.setDescription(description);
10 dto.setTitle(title);
11
12 return dto;
13 }
14}
The IntegrationTestUtil class has two purposes:
First, it declares a constact called APPLICATION_JSON_UTF8 that is used to set the content type and character set of HTTP requests and to verify the content type and character set of HTTP responses.
Second, it contains a static byte[] convertObjectsToJsonBytes(Object object) method that is used to convert object to bytes that contains the JSON representation of the object. The implementation of this method consists of following steps:
- Create a new ObjectMapper object.
- Configure the created object to include only non null properties of the serialized object.
- Convert the object as json string and return the created string as byte array.
The source code of the IntegrationTestUtil class looks as follows:
1import org.codehaus.jackson.map.ObjectMapper;
2import org.codehaus.jackson.map.annotate.JsonSerialize;
3import org.springframework.http.MediaType;
4
5import java.io.IOException;
6import java.nio.charset.Charset;
7
8public class IntegrationTestUtil {
9
10 public static final MediaType APPLICATION_JSON_UTF8 = new MediaType(MediaType.APPLICATION_JSON.getType(), MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
11
12 public static byte[] convertObjectToJsonBytes(Object object) throws IOException {
13 ObjectMapper mapper = new ObjectMapper();
14 mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
15 return mapper.writeValueAsBytes(object);
16 }
17}
The DBUnit dataset which is used to initialize our database into a known state before our tests are run is called toDoData.xml and its content looks as follows:
1<dataset>
2 <todos id="1" creation_time="2012-10-21 11:13:28" description="Lorem ipsum" modification_time="2012-10-21 11:13:28" title="Foo" version="0"/>
3 <todos id="2" creation_time="2012-10-21 11:13:28" description="Lorem ipsum" modification_time="2012-10-21 11:13:28" title="Bar" version="0"/>
4</dataset>
The implementation of the SecurityRequestPostProcessor class is copied from the samples of the spring-test-mvc project. Its static UserDetailsRequestPostProcessor userDetailsService(String username) method is used to create a security context for Spring Security and set the logged in user by using the configured UserDetailsService (The username given as parameter specifies the used principal).
Add Todo Entry as Logged In User
As an example, we write an integration test which ensures that a logged in user can add new todo entries to the database. We can write this test by following these steps:
- Use the @ExpectedDatabase annotation to ensure that a new todo entry is added to the database.
- Create a new TodoDTO object and set its title and description.
- Perform a POST request to url ‘/api/todo/’. Set the content type of the request to ‘application/json’. Set the character set of the request to ‘UTF8’. Transform the created TodoDTO object into correct format and send it in the body of the request. If you are migrating your tests to Spring Framework 3.2, remember to use the content() method of the MockHttpServletRequestBuilder class instead of the body() method.
- Use static userDetailsService() method of the SecurityRequestPostProcessor class to set the logged in user. The username given as a method parameter specifies the user that is used when the request is performed.
- Verify that the 200 HTTP status code is returned.
- Verify that the content type of the response is ‘application/json’ and its character set is ‘UTF8’. If you are migrating your tests to Spring Framework 3.2, remember to use the contentType() method of ContentResultMatchers class instead of the mimeType() method.
- Verify that the information of the added todo entry is returned correctly.
The source code of our integration test looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.ExpectedDatabase;
4import com.github.springtestdbunit.assertion.DatabaseAssertionMode;
5import org.junit.Before;
6import org.junit.Test;
7import org.junit.runner.RunWith;
8import org.springframework.security.web.FilterChainProxy;
9import org.springframework.test.context.ContextConfiguration;
10import org.springframework.test.context.TestExecutionListeners;
11import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
13import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
14import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
15import org.springframework.test.context.web.WebAppConfiguration;
16import org.springframework.test.web.servlet.MockMvc;
17import org.springframework.test.web.servlet.setup.MockMvcBuilders;
18import org.springframework.web.context.WebApplicationContext;
19
20import javax.annotation.Resource;
21
22import static org.springframework.test.web.server.samples.context.SecurityRequestPostProcessors.userDetailsService;
23import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
24import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
25import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
26
27@RunWith(SpringJUnit4ClassRunner.class)
28@ContextConfiguration(classes = {ExampleApplicationContext.class})
29//@ContextConfiguration(locations = {"classpath:exampleApplicationContext.xml"})
30@WebAppConfiguration
31@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
32 DirtiesContextTestExecutionListener.class,
33 TransactionalTestExecutionListener.class,
34 DbUnitTestExecutionListener.class })
35@DatabaseSetup("toDoData.xml")
36public class ITTodoControllerTest {
37
38 //Add FilterChainProxy and WebApplicationContext here
39
40 private MockMvc mockMvc;
41
42 //Add the setUp() method here
43
44 @Test
45 @ExpectedDatabase(value="toDoData-add-expected.xml", assertionMode = DatabaseAssertionMode.NON_STRICT)
46 public void addAsUser() throws Exception {
47 TodoDTO added = TodoTestUtil.createDTO(null, "description", "title");
48 mockMvc.perform(post("/api/todo")
49 .contentType(IntegrationTestUtil.APPLICATION_JSON_UTF8)
50 .content(IntegrationTestUtil.convertObjectToJsonBytes(added))
51 .with(userDetailsService("user"))
52 )
53 .andExpect(status().isOk())
54 .andExpect(content().contentType(IntegrationTestUtil.APPLICATION_JSON_UTF8))
55 .andExpect(content().string("{\"id\":3,\"description\":\"description\",\"title\":\"title\"}"));
56 }
57}
The DBUnit dataset file which is used to verify that a new todo entry is added to the database is toDoData-add-expected.xml and its content looks as follows:
1<dataset>
2 <todos id="1" description="Lorem ipsum" title="Foo" version="0"/>
3 <todos id="2" description="Lorem ipsum" title="Bar" version="0"/>
4 <todos id="3" description="description" title="title" version="0"/>
5</dataset>
That is All Folks!
We now know how we can migrate our existing integration tests from spring-test-mvc to Spring MVC Test Framework. This tutorial has taught us two things:
- We know how we can configure our integration tests when we are using Spring Framework 3.2.
- We know how we can migrate our existing integration tests to use the Spring MVC Test Framework instead of the spring-test-mvc project.
The example application of this blog entry provides more examples about different integration tests which use the Spring MVC Test Framework. As always, you can get the example application from Github.