Writing Integration Tests for a REST API - Writing Data
After we have finished this lesson, we
- Understand what kind of test data we should use when we write integration tests for controller methods that write data to the database.
- Know how we can write integration tests for controller methods that write data to the database.
This lesson assumes that:
- You can write assertions with Hamcrest
- You can write unit tests for REST controllers that write information to the database
- You know how you can configure your integration tests
- You know how you can create DbUnit data sets
- You know how you can use DbUnit data sets in your integration tests
- You can write integration tests for REST controllers that read information from the database
Introduction to the System Under Test
We have to write integration tests for a controller method that processes PUT requests send to the path: ‘/api/task/{taskId}’. This method is called update(), and it is implemented by following these steps:
- Read the information of the updated task from the request body and validate the information.
- If validation errors are found, return the HTTP status code 400 and describe the validation errors in the response body.
- Set the id of the updated task.
- Update the information of the task and return the information of the updated task.
The source code of the tested controller method looks as follows:
1import org.springframework.beans.factory.annotation.Autowired;
2import org.springframework.web.bind.annotation.PathVariable;
3import org.springframework.web.bind.annotation.RequestBody;
4import org.springframework.web.bind.annotation.RequestMapping;
5import org.springframework.web.bind.annotation.RequestMethod;
6import org.springframework.web.bind.annotation.RestController;
7
8import javax.validation.Valid;
9
10@RestController
11@RequestMapping("/api/task")
12public class TaskCrudController {
13
14 private final TaskCrudService crudService;
15
16 @Autowired
17 TaskCrudController(TaskCrudService crudService) {
18 this.crudService = crudService;
19 }
20
21 @RequestMapping(value= "{taskId}", method = RequestMethod.PUT)
22 public TaskDTO update(@PathVariable("taskId") Long taskId,
23 @Valid @RequestBody TaskFormDTO task) {
24 task.setId(taskId);
25 return crudService.update(task);
26 }
27}
The TaskFormDTO class contains the information that is used to create new tasks and update the information of existing tasks. It also declares the validation rules that are used to validate this information. Its source code looks as follows:
1import org.apache.commons.lang3.builder.ToStringBuilder;
2import org.hibernate.validator.constraints.NotBlank;
3
4import javax.validation.constraints.Size;
5
6public class TaskFormDTO {
7
8 private Long id;
9
10 @Size(max = 500)
11 private String description;
12
13 @NotBlank
14 @Size(max = 100)
15 private String title;
16}
The validation errors are processed by the TaskErrorHandler class. Its implementation doesn’t belong to the scope of this course, but it is useful to know what kind of a JSON document is returned to the client if validation fails. For example, if the client tries to update the information of a task by using an empty title, the following JSON document is returned to the client:
1{
2 "fieldErrors":[
3 {
4 "field":"title",
5 "errorCode":"NotBlank"
6 }
7 ]
8}
Additional Reading:
On the other hand, if the information of an open task is updated successfully, the following JSON document is returned to the client:
1{
2 "assigneeId":null,
3 "closerId":null,
4 "creationTime":"2016-12-04T11:41:28+02:00[Europe/Helsinki]",
5 "creatorId":1,
6 "description":"The old lesson was not good",
7 "id":2,
8 "modificationTime":"2016-12-03T21:14:28+02:00[Europe/Helsinki]",
9 "resolution":null,
10 "status":"OPEN",
11 "tags":[{
12 "id":2,
13 "name":"lesson"
14 }],
15 "title":"Rewrite an existing lesson"
16}
Let’s move on and create the DbUnit data set that initializes our database into a known state before our integration tests are run.
Initializing Our Database Into a Known State
When we are writing integration tests for methods that can make changes to our database, and we want to initialize our database into a known state, we should follow the principles described in the lesson: Writing Integration Tests for “Normal” Controllers - Forms .
This means that because we are writing integration tests for a controller method that updates the information of an existing task, we have to create a DbUnit data set that inserts one row into the tasks, tags, and tasks_tags tables.
The name of our data set file is task.xml, and its content looks as follows:
1<dataset>
2 <tasks id="2"
3 assignee_id="[null]"
4 closer_id="[null]"
5 creation_time="2016-12-4 11:41:28"
6 creator_id="1"
7 description="This lesson talks about integration testing"
8 modification_time="2016-12-4 11:41:28"
9 resolution="[null]"
10 status="OPEN"
11 title="Write lesson"
12 version="0"/>
13
14 <tags id="2"
15 creation_time="2016-12-3 11:41:28"
16 modification_time="2016-12-3 11:41:28"
17 name="lesson"
18 version="0"/>
19
20 <tasks_tags task_id="2" tag_id="2"/>
21</dataset>
Next, we will write integration tests which verify that our controller method is working as expected.
Writing Our Integration Tests
Scenario 1: Validation Fails
Before we can write our integration tests, we have to add test specific configuration into our test class. We can configure our test cases by following these steps:
- Configure the DbUnit data set that is used to insert test data into our database. Because we are writing integration tests for a method that simply updates the information of an existing task, every test method can use the same data set.
- Create a private method that updates the information of an existing task by using an empty title and description. This method returns a
ResultActionsobject, and its goal is to help us to reduce duplicate code which is often found from the test classes that use the Spring MVC Test framework.
After we have configured our test cases, the source code of our test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import org.junit.Before;
5import org.junit.experimental.categories.Category;
6import org.junit.runner.RunWith;
7import org.springframework.beans.factory.annotation.Autowired;
8import org.springframework.http.MediaType;
9import org.springframework.test.context.ActiveProfiles;
10import org.springframework.test.context.ContextConfiguration;
11import org.springframework.test.context.TestExecutionListeners;
12import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
13import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
14import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
15import org.springframework.test.context.web.ServletTestExecutionListener;
16import org.springframework.test.context.web.WebAppConfiguration;
17import org.springframework.test.web.servlet.MockMvc;
18import org.springframework.test.web.servlet.ResultActions;
19import org.springframework.test.web.servlet.setup.MockMvcBuilders;
20import org.springframework.web.context.WebApplicationContext;
21
22import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
23
24@RunWith(SpringJUnit4ClassRunner.class)
25@ContextConfiguration(classes = {IntegrationTestContext.class})
26@WebAppConfiguration
27@TestExecutionListeners({
28 DependencyInjectionTestExecutionListener.class,
29 TransactionalTestExecutionListener.class,
30 DbUnitTestExecutionListener.class,
31 ServletTestExecutionListener.class
32})
33@DatabaseSetup("task.xml")
34@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
35@Category(IntegrationTest.class)
36@ActiveProfiles(Profiles.INTEGRATION_TEST)
37public class UpdateTaskWhenValidationFails {
38
39 @Autowired
40 private WebApplicationContext webAppContext;
41
42 private MockMvc mockMvc;
43
44 @Before
45 public void configureSystemUnderTest() {
46 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
47 .build();
48 }
49
50 private ResultActions updateTaskWithEmptyData() throws Exception {
51 TaskFormDTO input = new TaskFormDTO();
52 input.setDescription("");
53 input.setId(Tasks.WriteLesson.ID);
54 input.setTitle("");
55
56 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
57 .contentType(MediaType.APPLICATION_JSON_UTF8)
58 .content(WebTestUtil.convertObjectToJsonBytes(input))
59 );
60 }
61}
The WebTestUtil class is a utility class that can transform objects into JSON bytes. If you don’t remember how it works, you should take a look at the lesson: Writing Unit Tests for a REST API – Writing Data
.
Also, if you don’t know how you can configure your integration tests, you should take a look at the Configuring Our Integration Tests topic.
After we have configured our test cases, we have to write five integration tests for the tested controller method.
First, we have to verify that it returns the HTTP status code 400. After we have written this integration test, the source code of our test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import org.junit.Before;
5import org.junit.Test;
6import org.junit.experimental.categories.Category;
7import org.junit.runner.RunWith;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.http.MediaType;
10import org.springframework.test.context.ActiveProfiles;
11import org.springframework.test.context.ContextConfiguration;
12import org.springframework.test.context.TestExecutionListeners;
13import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
15import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
16import org.springframework.test.context.web.ServletTestExecutionListener;
17import org.springframework.test.context.web.WebAppConfiguration;
18import org.springframework.test.web.servlet.MockMvc;
19import org.springframework.test.web.servlet.ResultActions;
20import org.springframework.test.web.servlet.setup.MockMvcBuilders;
21import org.springframework.web.context.WebApplicationContext;
22
23import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
24import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
25
26@RunWith(SpringJUnit4ClassRunner.class)
27@ContextConfiguration(classes = {IntegrationTestContext.class})
28@WebAppConfiguration
29@TestExecutionListeners({
30 DependencyInjectionTestExecutionListener.class,
31 TransactionalTestExecutionListener.class,
32 DbUnitTestExecutionListener.class,
33 ServletTestExecutionListener.class
34})
35@DatabaseSetup("task.xml")
36@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
37@Category(IntegrationTest.class)
38@ActiveProfiles(Profiles.INTEGRATION_TEST)
39public class UpdateTaskWhenValidationFails {
40
41 @Autowired
42 private WebApplicationContext webAppContext;
43
44 private MockMvc mockMvc;
45
46 @Before
47 public void configureSystemUnderTest() {
48 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
49 .build();
50 }
51
52 @Test
53 public void shouldReturnHttpStatusCodeBadRequest() throws Exception {
54 updateTaskWithEmptyData()
55 .andExpect(status().isBadRequest());
56 }
57
58 private ResultActions updateTaskWithEmptyData() throws Exception {
59 TaskFormDTO input = new TaskFormDTO();
60 input.setDescription("");
61 input.setId(Tasks.WriteLesson.ID);
62 input.setTitle("");
63
64 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
65 .contentType(MediaType.APPLICATION_JSON_UTF8)
66 .content(WebTestUtil.convertObjectToJsonBytes(input))
67 );
68 }
69}
Second, we have to ensure that it returns validation errors as JSON. After we have implemented this test method, the relevant part of our integration test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import org.junit.Before;
5import org.junit.Test;
6import org.junit.experimental.categories.Category;
7import org.junit.runner.RunWith;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.http.MediaType;
10import org.springframework.test.context.ActiveProfiles;
11import org.springframework.test.context.ContextConfiguration;
12import org.springframework.test.context.TestExecutionListeners;
13import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
15import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
16import org.springframework.test.context.web.ServletTestExecutionListener;
17import org.springframework.test.context.web.WebAppConfiguration;
18import org.springframework.test.web.servlet.MockMvc;
19import org.springframework.test.web.servlet.ResultActions;
20import org.springframework.test.web.servlet.setup.MockMvcBuilders;
21import org.springframework.web.context.WebApplicationContext;
22
23import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
24import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
25
26@RunWith(SpringJUnit4ClassRunner.class)
27@ContextConfiguration(classes = {IntegrationTestContext.class})
28@WebAppConfiguration
29@TestExecutionListeners({
30 DependencyInjectionTestExecutionListener.class,
31 TransactionalTestExecutionListener.class,
32 DbUnitTestExecutionListener.class,
33 ServletTestExecutionListener.class
34})
35@DatabaseSetup("task.xml")
36@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
37@Category(IntegrationTest.class)
38@ActiveProfiles(Profiles.INTEGRATION_TEST)
39public class UpdateTaskWhenValidationFails {
40
41 @Autowired
42 private WebApplicationContext webAppContext;
43
44 private MockMvc mockMvc;
45
46 @Before
47 public void configureSystemUnderTest() {
48 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
49 .build();
50 }
51
52 @Test
53 public void shouldReturnValidationErrorsAsJson() throws Exception {
54 updateTaskWithEmptyData()
55 .andExpect(
56 content().contentType(MediaType.APPLICATION_JSON_UTF8)
57 );
58 }
59
60 private ResultActions updateTaskWithEmptyData() throws Exception {
61 TaskFormDTO input = new TaskFormDTO();
62 input.setDescription("");
63 input.setId(Tasks.WriteLesson.ID);
64 input.setTitle("");
65
66 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
67 .contentType(MediaType.APPLICATION_JSON_UTF8)
68 .content(WebTestUtil.convertObjectToJsonBytes(input))
69 );
70 }
71}
Third, we have to verify that it returns one validation error. After we have written this integration test, the relevant part of our test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import org.junit.Before;
5import org.junit.Test;
6import org.junit.experimental.categories.Category;
7import org.junit.runner.RunWith;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.http.MediaType;
10import org.springframework.test.context.ActiveProfiles;
11import org.springframework.test.context.ContextConfiguration;
12import org.springframework.test.context.TestExecutionListeners;
13import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
15import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
16import org.springframework.test.context.web.ServletTestExecutionListener;
17import org.springframework.test.context.web.WebAppConfiguration;
18import org.springframework.test.web.servlet.MockMvc;
19import org.springframework.test.web.servlet.ResultActions;
20import org.springframework.test.web.servlet.setup.MockMvcBuilders;
21import org.springframework.web.context.WebApplicationContext;
22
23import static org.hamcrest.Matchers.hasSize;
24import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
25import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
26
27@RunWith(SpringJUnit4ClassRunner.class)
28@ContextConfiguration(classes = {IntegrationTestContext.class})
29@WebAppConfiguration
30@TestExecutionListeners({
31 DependencyInjectionTestExecutionListener.class,
32 TransactionalTestExecutionListener.class,
33 DbUnitTestExecutionListener.class,
34 ServletTestExecutionListener.class
35})
36@DatabaseSetup("task.xml")
37@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
38@Category(IntegrationTest.class)
39@ActiveProfiles(Profiles.INTEGRATION_TEST)
40public class UpdateTaskWhenValidationFails {
41
42 @Autowired
43 private WebApplicationContext webAppContext;
44
45 private MockMvc mockMvc;
46
47 @Before
48 public void configureSystemUnderTest() {
49 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
50 .build();
51 }
52
53 @Test
54 public void shouldReturnOneValidationError() throws Exception {
55 updateTaskWithEmptyData()
56 .andExpect(jsonPath("$.fieldErrors", hasSize(1)));
57 }
58
59 private ResultActions updateTaskWithEmptyData() throws Exception {
60 TaskFormDTO input = new TaskFormDTO();
61 input.setDescription("");
62 input.setId(Tasks.WriteLesson.ID);
63 input.setTitle("");
64
65 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
66 .contentType(MediaType.APPLICATION_JSON_UTF8)
67 .content(WebTestUtil.convertObjectToJsonBytes(input))
68 );
69 }
70}
Fourth, we have to ensure that it returns a validation error about an empty title. After we have implemented this test method, the relevant part of our integration test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import org.junit.Before;
5import org.junit.Test;
6import org.junit.experimental.categories.Category;
7import org.junit.runner.RunWith;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.http.MediaType;
10import org.springframework.test.context.ActiveProfiles;
11import org.springframework.test.context.ContextConfiguration;
12import org.springframework.test.context.TestExecutionListeners;
13import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
15import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
16import org.springframework.test.context.web.ServletTestExecutionListener;
17import org.springframework.test.context.web.WebAppConfiguration;
18import org.springframework.test.web.servlet.MockMvc;
19import org.springframework.test.web.servlet.ResultActions;
20import org.springframework.test.web.servlet.setup.MockMvcBuilders;
21import org.springframework.web.context.WebApplicationContext;
22
23import static org.hamcrest.Matchers.is;
24import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
25import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
26
27@RunWith(SpringJUnit4ClassRunner.class)
28@ContextConfiguration(classes = {IntegrationTestContext.class})
29@WebAppConfiguration
30@TestExecutionListeners({
31 DependencyInjectionTestExecutionListener.class,
32 TransactionalTestExecutionListener.class,
33 DbUnitTestExecutionListener.class,
34 ServletTestExecutionListener.class
35})
36@DatabaseSetup("task.xml")
37@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
38@Category(IntegrationTest.class)
39@ActiveProfiles(Profiles.INTEGRATION_TEST)
40public class UpdateTaskWhenValidationFails {
41
42 @Autowired
43 private WebApplicationContext webAppContext;
44
45 private MockMvc mockMvc;
46
47 @Before
48 public void configureSystemUnderTest() {
49 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
50 .build();
51 }
52
53 @Test
54 public void shouldReturnValidationErrorAboutEmptyTitle() throws Exception {
55 updateTaskWithEmptyData()
56 .andExpect(jsonPath("$.fieldErrors[0].field",
57 is("title")
58 ))
59 .andExpect(jsonPath("$.fieldErrors[0].errorCode",
60 is("NotBlank")
61 ));
62 }
63
64 private ResultActions updateTaskWithEmptyData() throws Exception {
65 TaskFormDTO input = new TaskFormDTO();
66 input.setDescription("");
67 input.setId(Tasks.WriteLesson.ID);
68 input.setTitle("");
69
70 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
71 .contentType(MediaType.APPLICATION_JSON_UTF8)
72 .content(WebTestUtil.convertObjectToJsonBytes(input))
73 );
74 }
75}
If you have to write an integration test which ensures that multiple validation errors are returned, you cannot use the technique described here because the validation errors are returned in random order. For example, if the updated task has too long title and description, you can write the required assertions by using the following code:
1.andExpect(jsonPath("$.fieldErrors[?(@.field == 'title')].errorCode",
2 contains("Size")
3))
4.andExpect(jsonPath("$.fieldErrors[?(@.field == 'description')].errorCode",
5 contains("Size")
6));
Fifth, we have to verify that it doesn’t update the task. In other words, we have to verify that our database contains the same data that was inserted into it before our integration test was run.
After we have written this integration test, the relevant part of our test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import com.github.springtestdbunit.annotation.ExpectedDatabase;
5import com.github.springtestdbunit.assertion.DatabaseAssertionMode;
6import org.junit.Before;
7import org.junit.Test;
8import org.junit.experimental.categories.Category;
9import org.junit.runner.RunWith;
10import org.springframework.beans.factory.annotation.Autowired;
11import org.springframework.http.MediaType;
12import org.springframework.test.context.ActiveProfiles;
13import org.springframework.test.context.ContextConfiguration;
14import org.springframework.test.context.TestExecutionListeners;
15import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
16import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
17import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
18import org.springframework.test.context.web.ServletTestExecutionListener;
19import org.springframework.test.context.web.WebAppConfiguration;
20import org.springframework.test.web.servlet.MockMvc;
21import org.springframework.test.web.servlet.ResultActions;
22import org.springframework.test.web.servlet.setup.MockMvcBuilders;
23import org.springframework.web.context.WebApplicationContext;
24
25import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
26
27@RunWith(SpringJUnit4ClassRunner.class)
28@ContextConfiguration(classes = {IntegrationTestContext.class})
29@WebAppConfiguration
30@TestExecutionListeners({
31 DependencyInjectionTestExecutionListener.class,
32 TransactionalTestExecutionListener.class,
33 DbUnitTestExecutionListener.class,
34 ServletTestExecutionListener.class
35})
36@DatabaseSetup("task.xml")
37@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
38@Category(IntegrationTest.class)
39@ActiveProfiles(Profiles.INTEGRATION_TEST)
40public class UpdateTaskWhenValidationFails {
41
42 @Autowired
43 private WebApplicationContext webAppContext;
44
45 private MockMvc mockMvc;
46
47 @Before
48 public void configureSystemUnderTest() {
49 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
50 .build();
51 }
52
53 @Test
54 @ExpectedDatabase(value = "task.xml",
55 assertionMode = DatabaseAssertionMode.NON_STRICT
56 )
57 public void shouldNotUpdateTask() throws Exception {
58 updateTaskWithEmptyData();
59 }
60
61 private ResultActions updateTaskWithEmptyData() throws Exception {
62 TaskFormDTO input = new TaskFormDTO();
63 input.setDescription("");
64 input.setId(Tasks.WriteLesson.ID);
65 input.setTitle("");
66
67 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
68 .contentType(MediaType.APPLICATION_JSON_UTF8)
69 .content(WebTestUtil.convertObjectToJsonBytes(input))
70 );
71 }
72}
Next, we will write integration tests which ensure that the tested controller method is working as expected when validation is successful.
Scenario 2: Validation Is Successful
Before we can write the actual integration tests, we have to add test specific configuration into our test class. We can configure our test cases by following these steps:
- Configure the used DbUnit data set. Again, all test methods can use the same data set, and we will use the DbUnit data set that we created in the beginning of this lesson.
- Declare the constants that contain the new title and description of the updated task.
- Create a private method that updates the information of an existing task by using the new title and description. This method must return a
ResultActionsobject.
After we have configured our test cases, the source code of our integration test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import org.junit.Before;
5import org.junit.experimental.categories.Category;
6import org.junit.runner.RunWith;
7import org.springframework.beans.factory.annotation.Autowired;
8import org.springframework.http.MediaType;
9import org.springframework.test.context.ActiveProfiles;
10import org.springframework.test.context.ContextConfiguration;
11import org.springframework.test.context.TestExecutionListeners;
12import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
13import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
14import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
15import org.springframework.test.context.web.ServletTestExecutionListener;
16import org.springframework.test.context.web.WebAppConfiguration;
17import org.springframework.test.web.servlet.MockMvc;
18import org.springframework.test.web.servlet.ResultActions;
19import org.springframework.test.web.servlet.setup.MockMvcBuilders;
20import org.springframework.web.context.WebApplicationContext;
21
22import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
23
24@RunWith(SpringJUnit4ClassRunner.class)
25@ContextConfiguration(classes = {IntegrationTestContext.class})
26@WebAppConfiguration
27@TestExecutionListeners({
28 DependencyInjectionTestExecutionListener.class,
29 TransactionalTestExecutionListener.class,
30 DbUnitTestExecutionListener.class,
31 ServletTestExecutionListener.class
32})
33@DatabaseSetup("task.xml")
34@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
35@Category(IntegrationTest.class)
36@ActiveProfiles(Profiles.INTEGRATION_TEST)
37public class UpdateTaskWhenValidationIsSuccessful {
38
39 private static final String NEW_DESCRIPTION = "The old lesson was not good";
40 private static final String NEW_TITLE = "Rewrite an existing lesson";
41
42 @Autowired
43 private WebApplicationContext webAppContext;
44
45 private MockMvc mockMvc;
46
47 @Before
48 public void configureSystemUnderTest() {
49 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
50 .build();
51 }
52
53 private ResultActions updateTask() throws Exception {
54 TaskFormDTO input = new TaskFormDTO();
55 input.setDescription(NEW_DESCRIPTION);
56 input.setId(Tasks.WriteLesson.ID);
57 input.setTitle(NEW_TITLE);
58
59 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
60 .contentType(MediaType.APPLICATION_JSON_UTF8)
61 .content(WebTestUtil.convertObjectToJsonBytes(input))
62 );
63 }
64}
There are two things that you should know before we can continue this lesson:
- We added these constants into our test class because they are used only by this test class.
- If you are writing integration tests for a controller method that inserts data into your database, you must reset the generated id column values before your test methods are run.
After we have configured our test cases, we have to write ten integration tests for the tested controller method.
First, we have to verify that it returns the HTTP status code 200. After we have written this integration test, the source code of our test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import org.junit.Before;
5import org.junit.Test;
6import org.junit.experimental.categories.Category;
7import org.junit.runner.RunWith;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.http.MediaType;
10import org.springframework.test.context.ActiveProfiles;
11import org.springframework.test.context.ContextConfiguration;
12import org.springframework.test.context.TestExecutionListeners;
13import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
15import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
16import org.springframework.test.context.web.ServletTestExecutionListener;
17import org.springframework.test.context.web.WebAppConfiguration;
18import org.springframework.test.web.servlet.MockMvc;
19import org.springframework.test.web.servlet.ResultActions;
20import org.springframework.test.web.servlet.setup.MockMvcBuilders;
21import org.springframework.web.context.WebApplicationContext;
22
23import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
24import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
25
26@RunWith(SpringJUnit4ClassRunner.class)
27@ContextConfiguration(classes = {IntegrationTestContext.class})
28@WebAppConfiguration
29@TestExecutionListeners({
30 DependencyInjectionTestExecutionListener.class,
31 TransactionalTestExecutionListener.class,
32 DbUnitTestExecutionListener.class,
33 ServletTestExecutionListener.class
34})
35@DatabaseSetup("task.xml")
36@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
37@Category(IntegrationTest.class)
38@ActiveProfiles(Profiles.INTEGRATION_TEST)
39public class UpdateTaskWhenValidationIsSuccessful {
40
41 private static final String NEW_DESCRIPTION = "The old lesson was not good";
42 private static final String NEW_TITLE = "Rewrite an existing lesson";
43
44 @Autowired
45 private WebApplicationContext webAppContext;
46
47 private MockMvc mockMvc;
48
49 @Before
50 public void configureSystemUnderTest() {
51 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
52 .build();
53 }
54
55 @Test
56 public void shouldReturnHttpStatusCodeOk() throws Exception {
57 updateTask()
58 .andExpect(status().isOk());
59 }
60
61 private ResultActions updateTask() throws Exception {
62 TaskFormDTO input = new TaskFormDTO();
63 input.setDescription(NEW_DESCRIPTION);
64 input.setId(Tasks.WriteLesson.ID);
65 input.setTitle(NEW_TITLE);
66
67 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
68 .contentType(MediaType.APPLICATION_JSON_UTF8)
69 .content(WebTestUtil.convertObjectToJsonBytes(input))
70 );
71 }
72}
Second, we have to ensure that it returns the information of the updated task as JSON. After we have implemented this test method, the relevant part of our test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import org.junit.Before;
5import org.junit.Test;
6import org.junit.experimental.categories.Category;
7import org.junit.runner.RunWith;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.http.MediaType;
10import org.springframework.test.context.ActiveProfiles;
11import org.springframework.test.context.ContextConfiguration;
12import org.springframework.test.context.TestExecutionListeners;
13import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
15import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
16import org.springframework.test.context.web.ServletTestExecutionListener;
17import org.springframework.test.context.web.WebAppConfiguration;
18import org.springframework.test.web.servlet.MockMvc;
19import org.springframework.test.web.servlet.ResultActions;
20import org.springframework.test.web.servlet.setup.MockMvcBuilders;
21import org.springframework.web.context.WebApplicationContext;
22
23import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
24import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
25
26@RunWith(SpringJUnit4ClassRunner.class)
27@ContextConfiguration(classes = {IntegrationTestContext.class})
28@WebAppConfiguration
29@TestExecutionListeners({
30 DependencyInjectionTestExecutionListener.class,
31 TransactionalTestExecutionListener.class,
32 DbUnitTestExecutionListener.class,
33 ServletTestExecutionListener.class
34})
35@DatabaseSetup("task.xml")
36@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
37@Category(IntegrationTest.class)
38@ActiveProfiles(Profiles.INTEGRATION_TEST)
39public class UpdateTaskWhenValidationIsSuccessful {
40
41 private static final String NEW_DESCRIPTION = "The old lesson was not good";
42 private static final String NEW_TITLE = "Rewrite an existing lesson";
43
44 @Autowired
45 private WebApplicationContext webAppContext;
46
47 private MockMvc mockMvc;
48
49 @Before
50 public void configureSystemUnderTest() {
51 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
52 .build();
53 }
54
55 @Test
56 public void shouldReturnUpdatedTaskAsJson() throws Exception {
57 updateTask()
58 .andExpect(
59 content().contentType(MediaType.APPLICATION_JSON_UTF8)
60 );
61 }
62
63 private ResultActions updateTask() throws Exception {
64 TaskFormDTO input = new TaskFormDTO();
65 input.setDescription(NEW_DESCRIPTION);
66 input.setId(Tasks.WriteLesson.ID);
67 input.setTitle(NEW_TITLE);
68
69 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
70 .contentType(MediaType.APPLICATION_JSON_UTF8)
71 .content(WebTestUtil.convertObjectToJsonBytes(input))
72 );
73 }
74}
Third, we have to verify that it returns the information of the updated task. After we have written this integration test, the relevant part of our integration test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import org.junit.Before;
5import org.junit.Test;
6import org.junit.experimental.categories.Category;
7import org.junit.runner.RunWith;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.http.MediaType;
10import org.springframework.test.context.ActiveProfiles;
11import org.springframework.test.context.ContextConfiguration;
12import org.springframework.test.context.TestExecutionListeners;
13import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
15import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
16import org.springframework.test.context.web.ServletTestExecutionListener;
17import org.springframework.test.context.web.WebAppConfiguration;
18import org.springframework.test.web.servlet.MockMvc;
19import org.springframework.test.web.servlet.ResultActions;
20import org.springframework.test.web.servlet.setup.MockMvcBuilders;
21import org.springframework.web.context.WebApplicationContext;
22
23import static org.hamcrest.Matchers.is;
24import static org.hamcrest.Matchers.nullValue;
25import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
26import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
27
28@RunWith(SpringJUnit4ClassRunner.class)
29@ContextConfiguration(classes = {IntegrationTestContext.class})
30@WebAppConfiguration
31@TestExecutionListeners({
32 DependencyInjectionTestExecutionListener.class,
33 TransactionalTestExecutionListener.class,
34 DbUnitTestExecutionListener.class,
35 ServletTestExecutionListener.class
36})
37@DatabaseSetup("task.xml")
38@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
39@Category(IntegrationTest.class)
40@ActiveProfiles(Profiles.INTEGRATION_TEST)
41public class UpdateTaskWhenValidationIsSuccessful {
42
43 private static final String NEW_DESCRIPTION = "The old lesson was not good";
44 private static final String NEW_TITLE = "Rewrite an existing lesson";
45
46 @Autowired
47 private WebApplicationContext webAppContext;
48
49 private MockMvc mockMvc;
50
51 @Before
52 public void configureSystemUnderTest() {
53 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
54 .build();
55 }
56
57 @Test
58 public void shouldReturnInformationOfUpdatedTask() throws Exception {
59 updateTask()
60 .andExpect(jsonPath("$.assigneeId", nullValue()))
61 .andExpect(jsonPath("$.closerId", nullValue()))
62 .andExpect(jsonPath("$.creationTime",
63 is(Tasks.WriteLesson.CREATION_TIME_STRING))
64 )
65 .andExpect(jsonPath("$.creatorId",
66 is(Tasks.WriteLesson.CREATOR_ID.intValue()))
67 )
68 .andExpect(jsonPath("$.id",
69 is(Tasks.WriteLesson.ID.intValue()))
70 )
71 .andExpect(jsonPath("$.modificationTime",
72 is(ConstantDateTimeService.CURRENT_DATE_AND_TIME))
73 )
74 .andExpect(jsonPath("$.title", is(NEW_TITLE)))
75 .andExpect(jsonPath("$.description", is(NEW_DESCRIPTION)))
76 .andExpect(jsonPath("$.status",
77 is(Tasks.WriteLesson.STATUS.toString()))
78 )
79 .andExpect(jsonPath("$.resolution", nullValue()));
80 }
81
82 private ResultActions updateTask() throws Exception {
83 TaskFormDTO input = new TaskFormDTO();
84 input.setDescription(NEW_DESCRIPTION);
85 input.setId(Tasks.WriteLesson.ID);
86 input.setTitle(NEW_TITLE);
87
88 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
89 .contentType(MediaType.APPLICATION_JSON_UTF8)
90 .content(WebTestUtil.convertObjectToJsonBytes(input))
91 );
92 }
93}
The ConstantDateTimeService is a bean that provides date and time information for the system under test when we run our integration tests (and the integrationTest Spring profile is active).
This information is used to set the creation and modification times of the created and updated tasks.
Fourth, we have ensure that it returns a task that has one tag. After we have implemented this test method, the relevant part of our integration test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import org.junit.Before;
5import org.junit.Test;
6import org.junit.experimental.categories.Category;
7import org.junit.runner.RunWith;
8import org.springframework.beans.factory.annotation.Autowired;
9import org.springframework.http.MediaType;
10import org.springframework.test.context.ActiveProfiles;
11import org.springframework.test.context.ContextConfiguration;
12import org.springframework.test.context.TestExecutionListeners;
13import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
14import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
15import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
16import org.springframework.test.context.web.ServletTestExecutionListener;
17import org.springframework.test.context.web.WebAppConfiguration;
18import org.springframework.test.web.servlet.MockMvc;
19import org.springframework.test.web.servlet.ResultActions;
20import org.springframework.test.web.servlet.setup.MockMvcBuilders;
21import org.springframework.web.context.WebApplicationContext;
22
23import static org.hamcrest.Matchers.hasSize;
24import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
25import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
26
27@RunWith(SpringJUnit4ClassRunner.class)
28@ContextConfiguration(classes = {IntegrationTestContext.class})
29@WebAppConfiguration
30@TestExecutionListeners({
31 DependencyInjectionTestExecutionListener.class,
32 TransactionalTestExecutionListener.class,
33 DbUnitTestExecutionListener.class,
34 ServletTestExecutionListener.class
35})
36@DatabaseSetup("task.xml")
37@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
38@Category(IntegrationTest.class)
39@ActiveProfiles(Profiles.INTEGRATION_TEST)
40public class UpdateTaskWhenValidationIsSuccessful {
41
42 private static final String NEW_DESCRIPTION = "The old lesson was not good";
43 private static final String NEW_TITLE = "Rewrite an existing lesson";
44
45 @Autowired
46 private WebApplicationContext webAppContext;
47
48 private MockMvc mockMvc;
49
50 @Before
51 public void configureSystemUnderTest() {
52 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
53 .build();
54 }
55
56 @Test
57 public void shouldReturnTaskThatHasOneTag() throws Exception {
58 updateTask()
59 .andExpect(jsonPath("$.tags", hasSize(1)));
60 }
61
62 private ResultActions updateTask() throws Exception {
63 TaskFormDTO input = new TaskFormDTO();
64 input.setDescription(NEW_DESCRIPTION);
65 input.setId(Tasks.WriteLesson.ID);
66 input.setTitle(NEW_TITLE);
67
68 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
69 .contentType(MediaType.APPLICATION_JSON_UTF8)
70 .content(WebTestUtil.convertObjectToJsonBytes(input))
71 );
72 }
73}
Fifth, we have to verify that it returns the information of the correct tag. After we have written this integration test, the relevant part of our test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import com.testwithspring.intermediate.IntegrationTest;
5import com.testwithspring.intermediate.IntegrationTestContext;
6import com.testwithspring.intermediate.ReplacementDataSetLoader;
7import com.testwithspring.intermediate.Tasks;
8import com.testwithspring.intermediate.common.ConstantDateTimeService;
9import com.testwithspring.intermediate.config.Profiles;
10import com.testwithspring.intermediate.task.TaskFormDTO;
11import org.junit.Before;
12import org.junit.Test;
13import org.junit.experimental.categories.Category;
14import org.junit.runner.RunWith;
15import org.springframework.beans.factory.annotation.Autowired;
16import org.springframework.http.MediaType;
17import org.springframework.test.context.ActiveProfiles;
18import org.springframework.test.context.ContextConfiguration;
19import org.springframework.test.context.TestExecutionListeners;
20import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
21import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
22import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
23import org.springframework.test.context.web.ServletTestExecutionListener;
24import org.springframework.test.context.web.WebAppConfiguration;
25import org.springframework.test.web.servlet.MockMvc;
26import org.springframework.test.web.servlet.ResultActions;
27import org.springframework.test.web.servlet.setup.MockMvcBuilders;
28import org.springframework.web.context.WebApplicationContext;
29
30import static org.hamcrest.Matchers.is;
31import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
32import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
33
34@RunWith(SpringJUnit4ClassRunner.class)
35@ContextConfiguration(classes = {IntegrationTestContext.class})
36@WebAppConfiguration
37@TestExecutionListeners({
38 DependencyInjectionTestExecutionListener.class,
39 TransactionalTestExecutionListener.class,
40 DbUnitTestExecutionListener.class,
41 ServletTestExecutionListener.class
42})
43@DatabaseSetup("task.xml")
44@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
45@Category(IntegrationTest.class)
46@ActiveProfiles(Profiles.INTEGRATION_TEST)
47public class UpdateTaskWhenValidationIsSuccessful {
48
49 private static final String NEW_DESCRIPTION = "The old lesson was not good";
50 private static final String NEW_TITLE = "Rewrite an existing lesson";
51
52 @Autowired
53 private WebApplicationContext webAppContext;
54
55 private MockMvc mockMvc;
56
57 @Before
58 public void configureSystemUnderTest() {
59 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
60 .build();
61 }
62
63 @Test
64 public void shouldReturnInformationOfCorrectTag() throws Exception {
65 updateTask()
66 .andExpect(jsonPath("$.tags[0].id",
67 is(Tasks.WriteLesson.Tags.Lesson.ID.intValue()))
68 )
69 .andExpect(jsonPath("$.tags[0].name",
70 is(Tasks.WriteLesson.Tags.Lesson.NAME))
71 );
72 }
73
74 private ResultActions updateTask() throws Exception {
75 TaskFormDTO input = new TaskFormDTO();
76 input.setDescription(NEW_DESCRIPTION);
77 input.setId(Tasks.WriteLesson.ID);
78 input.setTitle(NEW_TITLE);
79
80 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
81 .contentType(MediaType.APPLICATION_JSON_UTF8)
82 .content(WebTestUtil.convertObjectToJsonBytes(input))
83 );
84 }
85}
Sixth, we have to ensure that it updates the title and description of the updated task. After we have implemented this test method, the relevant part of our test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import com.github.springtestdbunit.annotation.ExpectedDatabase;
5import com.github.springtestdbunit.assertion.DatabaseAssertionMode;
6import org.junit.Before;
7import org.junit.Test;
8import org.junit.experimental.categories.Category;
9import org.junit.runner.RunWith;
10import org.springframework.beans.factory.annotation.Autowired;
11import org.springframework.http.MediaType;
12import org.springframework.test.context.ActiveProfiles;
13import org.springframework.test.context.ContextConfiguration;
14import org.springframework.test.context.TestExecutionListeners;
15import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
16import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
17import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
18import org.springframework.test.context.web.ServletTestExecutionListener;
19import org.springframework.test.context.web.WebAppConfiguration;
20import org.springframework.test.web.servlet.MockMvc;
21import org.springframework.test.web.servlet.ResultActions;
22import org.springframework.test.web.servlet.setup.MockMvcBuilders;
23import org.springframework.web.context.WebApplicationContext;
24
25import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
26
27@RunWith(SpringJUnit4ClassRunner.class)
28@ContextConfiguration(classes = {IntegrationTestContext.class})
29@WebAppConfiguration
30@TestExecutionListeners({
31 DependencyInjectionTestExecutionListener.class,
32 TransactionalTestExecutionListener.class,
33 DbUnitTestExecutionListener.class,
34 ServletTestExecutionListener.class
35})
36@DatabaseSetup("task.xml")
37@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
38@Category(IntegrationTest.class)
39@ActiveProfiles(Profiles.INTEGRATION_TEST)
40public class UpdateTaskWhenValidationIsSuccessful {
41
42 private static final String NEW_DESCRIPTION = "The old lesson was not good";
43 private static final String NEW_TITLE = "Rewrite an existing lesson";
44
45 @Autowired
46 private WebApplicationContext webAppContext;
47
48 private MockMvc mockMvc;
49
50 @Before
51 public void configureSystemUnderTest() {
52 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
53 .build();
54 }
55
56 @Test
57 @ExpectedDatabase(
58 value = "update-task-should-update-title-and-description.xml",
59 assertionMode = DatabaseAssertionMode.NON_STRICT
60 )
61 public void shouldUpdateTitleAndDescription() throws Exception {
62 updateTask();
63 }
64
65 private ResultActions updateTask() throws Exception {
66 TaskFormDTO input = new TaskFormDTO();
67 input.setDescription(NEW_DESCRIPTION);
68 input.setId(Tasks.WriteLesson.ID);
69 input.setTitle(NEW_TITLE);
70
71 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
72 .contentType(MediaType.APPLICATION_JSON_UTF8)
73 .content(WebTestUtil.convertObjectToJsonBytes(input))
74 );
75 }
76}
The source code of our DbUnit data set (update-task-should-update-title-and-description.xml) looks as follows:
1<dataset>
2 <tasks id="2"
3 description="The old lesson was not good"
4 title="Rewrite an existing lesson"/>
5</dataset>
Seventh, we have to verify that it updates the modificationTime and version of the updated task. After we have written this integration test, the relevant part of our test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import com.github.springtestdbunit.annotation.ExpectedDatabase;
5import com.github.springtestdbunit.assertion.DatabaseAssertionMode;
6import org.junit.Before;
7import org.junit.Test;
8import org.junit.experimental.categories.Category;
9import org.junit.runner.RunWith;
10import org.springframework.beans.factory.annotation.Autowired;
11import org.springframework.http.MediaType;
12import org.springframework.test.context.ActiveProfiles;
13import org.springframework.test.context.ContextConfiguration;
14import org.springframework.test.context.TestExecutionListeners;
15import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
16import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
17import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
18import org.springframework.test.context.web.ServletTestExecutionListener;
19import org.springframework.test.context.web.WebAppConfiguration;
20import org.springframework.test.web.servlet.MockMvc;
21import org.springframework.test.web.servlet.ResultActions;
22import org.springframework.test.web.servlet.setup.MockMvcBuilders;
23import org.springframework.web.context.WebApplicationContext;
24
25import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
26
27@RunWith(SpringJUnit4ClassRunner.class)
28@ContextConfiguration(classes = {IntegrationTestContext.class})
29@WebAppConfiguration
30@TestExecutionListeners({
31 DependencyInjectionTestExecutionListener.class,
32 TransactionalTestExecutionListener.class,
33 DbUnitTestExecutionListener.class,
34 ServletTestExecutionListener.class
35})
36@DatabaseSetup("task.xml")
37@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
38@Category(IntegrationTest.class)
39@ActiveProfiles(Profiles.INTEGRATION_TEST)
40public class UpdateTaskWhenValidationIsSuccessful {
41
42 private static final String NEW_DESCRIPTION = "The old lesson was not good";
43 private static final String NEW_TITLE = "Rewrite an existing lesson";
44
45 @Autowired
46 private WebApplicationContext webAppContext;
47
48 private MockMvc mockMvc;
49
50 @Before
51 public void configureSystemUnderTest() {
52 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
53 .build();
54 }
55
56 @Test
57 @ExpectedDatabase(value = "update-task-should-update-lifecycle-fields.xml",
58 assertionMode = DatabaseAssertionMode.NON_STRICT
59 )
60 public void shouldUpdateModificationTimeAndVersion() throws Exception {
61 updateTask();
62 }
63
64 private ResultActions updateTask() throws Exception {
65 TaskFormDTO input = new TaskFormDTO();
66 input.setDescription(NEW_DESCRIPTION);
67 input.setId(Tasks.WriteLesson.ID);
68 input.setTitle(NEW_TITLE);
69
70 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
71 .contentType(MediaType.APPLICATION_JSON_UTF8)
72 .content(WebTestUtil.convertObjectToJsonBytes(input))
73 );
74 }
75}
The source code of our DbUnit data set (update-task-should-update-lifecycle-fields.xml) looks as follows:
1<dataset>
2 <!-- Only the modification time and version should be updated -->
3 <tasks id="2"
4 creation_time="2016-12-4 11:41:28"
5 creator_id="1"
6 modification_time="2016-12-03 21:14:28.0"
7 version="1"/>
8</dataset>
Eighth, we have to ensure that it doesn’t change the status of the updated task. After we have implemented this test method, the relevant part of our integration test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import com.github.springtestdbunit.annotation.ExpectedDatabase;
5import com.github.springtestdbunit.assertion.DatabaseAssertionMode;
6import org.junit.Before;
7import org.junit.Test;
8import org.junit.experimental.categories.Category;
9import org.junit.runner.RunWith;
10import org.springframework.beans.factory.annotation.Autowired;
11import org.springframework.http.MediaType;
12import org.springframework.test.context.ActiveProfiles;
13import org.springframework.test.context.ContextConfiguration;
14import org.springframework.test.context.TestExecutionListeners;
15import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
16import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
17import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
18import org.springframework.test.context.web.ServletTestExecutionListener;
19import org.springframework.test.context.web.WebAppConfiguration;
20import org.springframework.test.web.servlet.MockMvc;
21import org.springframework.test.web.servlet.ResultActions;
22import org.springframework.test.web.servlet.setup.MockMvcBuilders;
23import org.springframework.web.context.WebApplicationContext;
24
25import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
26
27@RunWith(SpringJUnit4ClassRunner.class)
28@ContextConfiguration(classes = {IntegrationTestContext.class})
29@WebAppConfiguration
30@TestExecutionListeners({
31 DependencyInjectionTestExecutionListener.class,
32 TransactionalTestExecutionListener.class,
33 DbUnitTestExecutionListener.class,
34 ServletTestExecutionListener.class
35})
36@DatabaseSetup("task.xml")
37@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
38@Category(IntegrationTest.class)
39@ActiveProfiles(Profiles.INTEGRATION_TEST)
40public class UpdateTaskWhenValidationIsSuccessful {
41
42 private static final String NEW_DESCRIPTION = "The old lesson was not good";
43 private static final String NEW_TITLE = "Rewrite an existing lesson";
44
45 @Autowired
46 private WebApplicationContext webAppContext;
47
48 private MockMvc mockMvc;
49
50 @Before
51 public void configureSystemUnderTest() {
52 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
53 .build();
54 }
55
56 @Test
57 @ExpectedDatabase(value = "update-task-should-not-change-status.xml",
58 assertionMode = DatabaseAssertionMode.NON_STRICT
59 )
60 public void shouldNotChangeStatus() throws Exception {
61 updateTask();
62 }
63
64 private ResultActions updateTask() throws Exception {
65 TaskFormDTO input = new TaskFormDTO();
66 input.setDescription(NEW_DESCRIPTION);
67 input.setId(Tasks.WriteLesson.ID);
68 input.setTitle(NEW_TITLE);
69
70 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
71 .contentType(MediaType.APPLICATION_JSON_UTF8)
72 .content(WebTestUtil.convertObjectToJsonBytes(input))
73 );
74 }
75}
The source code of our DbUnit data set (update-task-should-not-change-status.xml) looks as follows:
1<dataset>
2 <tasks id="2"
3 closer_id="[null]"
4 resolution="[null]"
5 status="OPEN"/>
6</dataset>
Ninth, we have to verify that it doesn’t change the assignee of the updated task. After we have written this integration test, the source code of our test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import com.github.springtestdbunit.annotation.ExpectedDatabase;
5import com.github.springtestdbunit.assertion.DatabaseAssertionMode;
6import org.junit.Before;
7import org.junit.Test;
8import org.junit.experimental.categories.Category;
9import org.junit.runner.RunWith;
10import org.springframework.beans.factory.annotation.Autowired;
11import org.springframework.http.MediaType;
12import org.springframework.test.context.ActiveProfiles;
13import org.springframework.test.context.ContextConfiguration;
14import org.springframework.test.context.TestExecutionListeners;
15import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
16import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
17import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
18import org.springframework.test.context.web.ServletTestExecutionListener;
19import org.springframework.test.context.web.WebAppConfiguration;
20import org.springframework.test.web.servlet.MockMvc;
21import org.springframework.test.web.servlet.ResultActions;
22import org.springframework.test.web.servlet.setup.MockMvcBuilders;
23import org.springframework.web.context.WebApplicationContext;
24
25import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
26
27@RunWith(SpringJUnit4ClassRunner.class)
28@ContextConfiguration(classes = {IntegrationTestContext.class})
29@WebAppConfiguration
30@TestExecutionListeners({
31 DependencyInjectionTestExecutionListener.class,
32 TransactionalTestExecutionListener.class,
33 DbUnitTestExecutionListener.class,
34 ServletTestExecutionListener.class
35})
36@DatabaseSetup("task.xml")
37@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
38@Category(IntegrationTest.class)
39@ActiveProfiles(Profiles.INTEGRATION_TEST)
40public class UpdateTaskWhenValidationIsSuccessful {
41
42 private static final String NEW_DESCRIPTION = "The old lesson was not good";
43 private static final String NEW_TITLE = "Rewrite an existing lesson";
44
45 @Autowired
46 private WebApplicationContext webAppContext;
47
48 private MockMvc mockMvc;
49
50 @Before
51 public void configureSystemUnderTest() {
52 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
53 .build();
54 }
55
56 @Test
57 @ExpectedDatabase(value = "update-task-should-not-change-assignee.xml",
58 assertionMode = DatabaseAssertionMode.NON_STRICT
59 )
60 public void shouldNotChangeAssignee() throws Exception {
61 updateTask();
62 }
63
64 private ResultActions updateTask() throws Exception {
65 TaskFormDTO input = new TaskFormDTO();
66 input.setDescription(NEW_DESCRIPTION);
67 input.setId(Tasks.WriteLesson.ID);
68 input.setTitle(NEW_TITLE);
69
70 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
71 .contentType(MediaType.APPLICATION_JSON_UTF8)
72 .content(WebTestUtil.convertObjectToJsonBytes(input))
73 );
74 }
75}
The source code of our DbUnit data set (update-task-should-not-change-assignee.xml) looks as follows:
1<dataset>
2 <tasks id="2"
3 assignee_id="[null]"/>
4</dataset>
Tenth, we have to ensure that it doesn’t change the tags of the updated task. After we have implemented this test method, the relevant part of our test class looks as follows:
1import com.github.springtestdbunit.DbUnitTestExecutionListener;
2import com.github.springtestdbunit.annotation.DatabaseSetup;
3import com.github.springtestdbunit.annotation.DbUnitConfiguration;
4import com.github.springtestdbunit.annotation.ExpectedDatabase;
5import com.github.springtestdbunit.assertion.DatabaseAssertionMode;
6import org.junit.Before;
7import org.junit.Test;
8import org.junit.experimental.categories.Category;
9import org.junit.runner.RunWith;
10import org.springframework.beans.factory.annotation.Autowired;
11import org.springframework.http.MediaType;
12import org.springframework.test.context.ActiveProfiles;
13import org.springframework.test.context.ContextConfiguration;
14import org.springframework.test.context.TestExecutionListeners;
15import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
16import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
17import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
18import org.springframework.test.context.web.ServletTestExecutionListener;
19import org.springframework.test.context.web.WebAppConfiguration;
20import org.springframework.test.web.servlet.MockMvc;
21import org.springframework.test.web.servlet.ResultActions;
22import org.springframework.test.web.servlet.setup.MockMvcBuilders;
23import org.springframework.web.context.WebApplicationContext;
24
25import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
26
27@RunWith(SpringJUnit4ClassRunner.class)
28@ContextConfiguration(classes = {IntegrationTestContext.class})
29@WebAppConfiguration
30@TestExecutionListeners({
31 DependencyInjectionTestExecutionListener.class,
32 TransactionalTestExecutionListener.class,
33 DbUnitTestExecutionListener.class,
34 ServletTestExecutionListener.class
35})
36@DatabaseSetup("task.xml")
37@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
38@Category(IntegrationTest.class)
39@ActiveProfiles(Profiles.INTEGRATION_TEST)
40public class UpdateTaskWhenValidationIsSuccessful {
41
42 private static final String NEW_DESCRIPTION = "The old lesson was not good";
43 private static final String NEW_TITLE = "Rewrite an existing lesson";
44
45 @Autowired
46 private WebApplicationContext webAppContext;
47
48 private MockMvc mockMvc;
49
50 @Before
51 public void configureSystemUnderTest() {
52 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
53 .build();
54 }
55
56 @Test
57 @ExpectedDatabase(value = "update-task-should-not-change-tags.xml",
58 assertionMode = DatabaseAssertionMode.NON_STRICT
59 )
60 public void shouldNotMakeAnyChangesToTagsOfUpdatedTask() throws Exception {
61 updateTask();
62 }
63
64 private ResultActions updateTask() throws Exception {
65 TaskFormDTO input = new TaskFormDTO();
66 input.setDescription(NEW_DESCRIPTION);
67 input.setId(Tasks.WriteLesson.ID);
68 input.setTitle(NEW_TITLE);
69
70 return mockMvc.perform(put("/api/task/{taskId}", Tasks.WriteLesson.ID)
71 .contentType(MediaType.APPLICATION_JSON_UTF8)
72 .content(WebTestUtil.convertObjectToJsonBytes(input))
73 );
74 }
75}
The source code of our DbUnit data set (update-task-should-not-change-tags.xml) looks as follows:
1<dataset>
2 <tags id="2"
3 creation_time="2016-12-3 11:41:28"
4 modification_time="2016-12-3 11:41:28"
5 name="lesson"
6 version="0"/>
7
8 <tasks_tags task_id="2" tag_id="2"/>
9</dataset>
We have now written integration tests which help us to verify that the update() method of the TaskCrudController class is working as expected. Let’s summarize what we learned from this lesson.
Summary
This lesson has taught us three things:
- If we are writing integration tests for a controller method that inserts data into our database, we must reset the generated id column values before our test methods are run.
- If we want to write assertions which verify that multiple validation errors are returned, we must remember that validation errors are returned in random order.
- When we write assertions for the data found from our database, we should use small data sets that assert one logical concept.