Writing Integration Tests for "Normal" Controllers - Forms
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 processes form submissions.
- Know how we can write integration tests for controller methods that processes form submissions.
This lesson assumes that:
- You can write assertions with Hamcrest
- You can write unit tests for “normal” controllers that processes form submissions
- 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 “normal” controllers which render data by using views
Introduction to System Under Test
We have to write integration tests for a controller method that processes POST requests send to the path: ‘/task/{taskId}/update’. The name of this method is processUpdateTaskForm(), and it is implemented by following these steps:
- If the submitted form has validation errors, return the name of the form view (’task/update’).
- Update the information of the task.
- Create a feedback message which states that the information of the task was updated and ensure that this message is shown when the next view is rendered.
- Redirect the request to the view that renders 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.context.MessageSource;
3import org.springframework.stereotype.Controller;
4import org.springframework.validation.BindingResult;
5import org.springframework.web.bind.annotation.ModelAttribute;
6import org.springframework.web.bind.annotation.RequestMapping;
7import org.springframework.web.bind.annotation.RequestMethod;
8import org.springframework.web.servlet.mvc.support.RedirectAttributes;
9
10import javax.validation.Valid;
11import java.util.Locale;
12
13@Controller
14public class TaskCrudController {
15
16 private final TaskCrudService crudService;
17 private final MessageSource messageSource;
18
19 @Autowired
20 TaskCrudController(TaskCrudService crudService, MessageSource messageSource) {
21 this.crudService = crudService;
22 this.messageSource = messageSource;
23 }
24
25 @RequestMapping(value = "/task/{taskId}/update", method = RequestMethod.POST)
26 public String processUpdateTaskForm(@Valid @ModelAttribute("task") TaskFormDTO formObject,
27 BindingResult bindingResult,
28 RedirectAttributes redirectAttributes,
29 Locale currentLocale) {
30 if (bindingResult.hasErrors()) {
31 return "task/update";
32 }
33
34 TaskDTO updated = crudService.update(formObject);
35 addFeedbackMessage(redirectAttributes,
36 "feedback.message.task.updated",
37 currentLocale,
38 updated.getTitle()
39 );
40
41 redirectAttributes.addAttribute("taskId", updated.getId());
42 return "redirect:/task/{taskId}";
43 }
44
45 private void addFeedbackMessage(RedirectAttributes attributes,
46 String messageCode,
47 Locale currentLocale,
48 Object... messageParameters) {
49 String feedbackMessage = messageSource.getMessage(messageCode,
50 messageParameters,
51 currentLocale
52 );
53 attributes.addFlashAttribute("feedbackMessage", feedbackMessage);
54 }
55}
The TaskFormDTO class is the form object 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 the form object. The source code of the TaskFormDTO class looks as follows:
1import org.hibernate.validator.constraints.NotBlank;
2
3import javax.validation.constraints.Size;
4
5public class TaskFormDTO {
6
7 private Long id;
8
9 @Size(max = 500)
10 private String description;
11
12 @NotBlank
13 @Size(max = 100)
14 private String title;
15
16 //Getters and setters are omitted
17}
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 have to know that DbUnit doesn’t have an assertion mode that allows us to ignore the row count of the database tables that are found from our data sets.
In other words, when we write assertions for the rows of a database table, we have to specify the column values of all table rows that were inserted into our database before our integration tests were run.
Like I mentioned before , we should create small data sets that are easy to read, write and maintain. That is why I think that we should follow these two rules:
- If we are writing integration tests for a method that inserts new rows into our database tables, we should use a DbUnit data set that doesn’t insert any rows into these tables.
- If we are writing integration tests for a method that updates existing rows of our database tables, we should use a DbUnit data set that inserts one row into the updated tables.
If we follow these rules, we can use small data sets or divide our data sets into multiple smaller data sets because the database tables whose data was changed by the tested code always have only one row.
In other words, 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 this 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 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 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 should use the same data set.
- Create a private method that submits an empty update task form. 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.test.context.ActiveProfiles;
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.transaction.TransactionalTestExecutionListener;
14import org.springframework.test.context.web.ServletTestExecutionListener;
15import org.springframework.test.context.web.WebAppConfiguration;
16import org.springframework.test.web.servlet.MockMvc;
17import org.springframework.test.web.servlet.ResultActions;
18import org.springframework.test.web.servlet.setup.MockMvcBuilders;
19import org.springframework.web.context.WebApplicationContext;
20
21import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
22
23@RunWith(SpringJUnit4ClassRunner.class)
24@ContextConfiguration(classes = {IntegrationTestContext.class})
25@WebAppConfiguration
26@TestExecutionListeners({
27 DependencyInjectionTestExecutionListener.class,
28 TransactionalTestExecutionListener.class,
29 DbUnitTestExecutionListener.class,
30 ServletTestExecutionListener.class
31})
32@DatabaseSetup("task.xml")
33@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
34@Category(IntegrationTest.class)
35@ActiveProfiles(Profiles.INTEGRATION_TEST)
36public class ProcessUpdateTaskFormWhenValidationFailTest {
37
38 @Autowired
39 private WebApplicationContext webAppContext;
40
41 private MockMvc mockMvc;
42
43 @Before
44 public void configureSystemUnderTest() {
45 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
46 .build();
47 }
48
49 private ResultActions submitEmptyUpdateTaskForm() throws Exception {
50 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
51 .param("description", "")
52 .param("id", Tasks.WriteLesson.ID.toString())
53 .param("title", "")
54 );
55 }
56}
After we have configured our test cases, we have to write seven integration tests for the processUpdateTaskForm() 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 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.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.post;
23import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
24
25@RunWith(SpringJUnit4ClassRunner.class)
26@ContextConfiguration(classes = {IntegrationTestContext.class})
27@WebAppConfiguration
28@TestExecutionListeners({
29 DependencyInjectionTestExecutionListener.class,
30 TransactionalTestExecutionListener.class,
31 DbUnitTestExecutionListener.class,
32 ServletTestExecutionListener.class
33})
34@DatabaseSetup("task.xml")
35@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
36@Category(IntegrationTest.class)
37@ActiveProfiles(Profiles.INTEGRATION_TEST)
38public class ProcessUpdateTaskFormWhenValidationFailTest {
39
40 @Autowired
41 private WebApplicationContext webAppContext;
42
43 private MockMvc mockMvc;
44
45 @Before
46 public void configureSystemUnderTest() {
47 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
48 .build();
49 }
50
51 @Test
52 public void shouldReturnHttpStatusCodeOk() throws Exception {
53 submitEmptyUpdateTaskForm()
54 .andExpect(status().isOk());
55 }
56
57 private ResultActions submitEmptyUpdateTaskForm() throws Exception {
58 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
59 .param("description", "")
60 .param("id", Tasks.WriteLesson.ID.toString())
61 .param("title", "")
62 );
63 }
64}
Second, we have to ensure that it renders the update task view. 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.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.post;
23import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
24
25@RunWith(SpringJUnit4ClassRunner.class)
26@ContextConfiguration(classes = {IntegrationTestContext.class})
27@WebAppConfiguration
28@TestExecutionListeners({
29 DependencyInjectionTestExecutionListener.class,
30 TransactionalTestExecutionListener.class,
31 DbUnitTestExecutionListener.class,
32 ServletTestExecutionListener.class
33})
34@DatabaseSetup("task.xml")
35@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
36@Category(IntegrationTest.class)
37@ActiveProfiles(Profiles.INTEGRATION_TEST)
38public class ProcessUpdateTaskFormWhenValidationFailTest {
39
40 @Autowired
41 private WebApplicationContext webAppContext;
42
43 private MockMvc mockMvc;
44
45 @Before
46 public void configureSystemUnderTest() {
47 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
48 .build();
49 }
50
51 @Test
52 public void shouldRenderUpdateTaskView() throws Exception {
53 submitEmptyUpdateTaskForm()
54 .andExpect(view().name("task/update"));
55 }
56
57 private ResultActions submitEmptyUpdateTaskForm() throws Exception {
58 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
59 .param("description", "")
60 .param("id", Tasks.WriteLesson.ID.toString())
61 .param("title", "")
62 );
63 }
64}
Third, we have to verify that it forwards the user to the update task page. 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.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.post;
23import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
24
25@RunWith(SpringJUnit4ClassRunner.class)
26@ContextConfiguration(classes = {IntegrationTestContext.class})
27@WebAppConfiguration
28@TestExecutionListeners({
29 DependencyInjectionTestExecutionListener.class,
30 TransactionalTestExecutionListener.class,
31 DbUnitTestExecutionListener.class,
32 ServletTestExecutionListener.class
33})
34@DatabaseSetup("task.xml")
35@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
36@Category(IntegrationTest.class)
37@ActiveProfiles(Profiles.INTEGRATION_TEST)
38public class ProcessUpdateTaskFormWhenValidationFailTest {
39
40 @Autowired
41 private WebApplicationContext webAppContext;
42
43 private MockMvc mockMvc;
44
45 @Before
46 public void configureSystemUnderTest() {
47 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
48 .build();
49 }
50
51 @Test
52 public void shouldForwardUserToUpdateTaskPageUrl() throws Exception {
53 submitEmptyUpdateTaskForm()
54 .andExpect(forwardedUrl("/WEB-INF/jsp/task/update.jsp"));
55 }
56
57 private ResultActions submitEmptyUpdateTaskForm() throws Exception {
58 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
59 .param("description", "")
60 .param("id", Tasks.WriteLesson.ID.toString())
61 .param("title", "")
62 );
63 }
64}
Fourth, we have to ensure that it shows 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.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.hamcrest.Matchers.is;
23import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
24import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl;
25import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
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 ProcessUpdateTaskFormWhenValidationFailTest {
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 shouldShowValidationErrorAboutEmptyTitle() throws Exception {
55 submitEmptyUpdateTaskForm()
56 .andExpect(model().attributeHasFieldErrorCode("task",
57 "title",
58 is("NotBlank")
59 ));
60 }
61
62 private ResultActions submitEmptyUpdateTaskForm() throws Exception {
63 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
64 .param("description", "")
65 .param("id", Tasks.WriteLesson.ID.toString())
66 .param("title", "")
67 );
68 }
69}
Fifth, we have to ensure that it shows the field values of the update task form. 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.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.hamcrest.Matchers.allOf;
23import static org.hamcrest.Matchers.hasProperty;
24import static org.hamcrest.Matchers.is;
25import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
26import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
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 ProcessUpdateTaskFormWhenValidationFailTest {
42
43 @Autowired
44 private WebApplicationContext webAppContext;
45
46 private MockMvc mockMvc;
47
48 @Before
49 public void configureSystemUnderTest() {
50 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
51 .build();
52 }
53
54 @Test
55 public void shouldShowFieldValuesOfUpdateTaskForm() throws Exception {
56 submitEmptyUpdateTaskForm()
57 .andExpect(model().attribute("task", allOf(
58 hasProperty("description", is("")),
59 hasProperty("title", is(""))
60 )));
61 }
62
63 private ResultActions submitEmptyUpdateTaskForm() throws Exception {
64 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
65 .param("description", "")
66 .param("id", Tasks.WriteLesson.ID.toString())
67 .param("title", "")
68 );
69 }
70}
Sixth, we have to verify that it doesn’t modify the hidden id parameter. 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.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.hamcrest.Matchers.allOf;
23import static org.hamcrest.Matchers.hasProperty;
24import static org.hamcrest.Matchers.is;
25import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
26import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
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 ProcessUpdateTaskFormWhenValidationFailTest {
42
43 @Autowired
44 private WebApplicationContext webAppContext;
45
46 private MockMvc mockMvc;
47
48 @Before
49 public void configureSystemUnderTest() {
50 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
51 .build();
52 }
53
54 @Test
55 public void shouldNotModifyHiddenIdParameter() throws Exception {
56 submitEmptyUpdateTaskForm()
57 .andExpect(model().attribute("task",
58 hasProperty("id", is(Tasks.WriteLesson.ID))
59 ));
60 }
61
62 private ResultActions submitEmptyUpdateTaskForm() throws Exception {
63 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
64 .param("description", "")
65 .param("id", Tasks.WriteLesson.ID.toString())
66 .param("title", "")
67 );
68 }
69}
Seventh, 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.test.context.ActiveProfiles;
12import org.springframework.test.context.ContextConfiguration;
13import org.springframework.test.context.TestExecutionListeners;
14import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
15import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
16import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
17import org.springframework.test.context.web.ServletTestExecutionListener;
18import org.springframework.test.context.web.WebAppConfiguration;
19import org.springframework.test.web.servlet.MockMvc;
20import org.springframework.test.web.servlet.ResultActions;
21import org.springframework.test.web.servlet.setup.MockMvcBuilders;
22import org.springframework.web.context.WebApplicationContext;
23
24import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
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 ProcessUpdateTaskFormWhenValidationFailTest {
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 @ExpectedDatabase(value = "task.xml",
54 assertionMode = DatabaseAssertionMode.NON_STRICT
55 )
56 public void shouldNotUpdateTask() throws Exception {
57 submitEmptyUpdateTaskForm();
58 }
59
60 private ResultActions submitEmptyUpdateTaskForm() throws Exception {
61 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
62 .param("description", "")
63 .param("id", Tasks.WriteLesson.ID.toString())
64 .param("title", "")
65 );
66 }
67}
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 constant that contains the expected feedback message that is read from the localization file.
- Declare the constants that contain the new title and description of the updated task.
- Create a private method that submits the update task form by using the new information.
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.test.context.ActiveProfiles;
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.transaction.TransactionalTestExecutionListener;
14import org.springframework.test.context.web.ServletTestExecutionListener;
15import org.springframework.test.context.web.WebAppConfiguration;
16import org.springframework.test.web.servlet.MockMvc;
17import org.springframework.test.web.servlet.ResultActions;
18import org.springframework.test.web.servlet.setup.MockMvcBuilders;
19import org.springframework.web.context.WebApplicationContext;
20
21import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
22
23@RunWith(SpringJUnit4ClassRunner.class)
24@ContextConfiguration(classes = {IntegrationTestContext.class})
25@WebAppConfiguration
26@TestExecutionListeners({
27 DependencyInjectionTestExecutionListener.class,
28 TransactionalTestExecutionListener.class,
29 DbUnitTestExecutionListener.class,
30 ServletTestExecutionListener.class
31})
32@DatabaseSetup("task.xml")
33@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
34@Category(IntegrationTest.class)
35@ActiveProfiles(Profiles.INTEGRATION_TEST)
36public class ProcessUpdateTaskFormWhenValidationIsSuccessfulTest {
37
38 private static final String FEEDBACK_MESSAGE_TASK_UPDATED = "The information of a task was updated successfully.";
39
40 private static final String NEW_DESCRIPTION = "The old lesson was not good";
41 private static final String NEW_TITLE = "Rewrite an existing lesson";
42
43 @Autowired
44 private WebApplicationContext webAppContext;
45
46 private MockMvc mockMvc;
47
48 @Before
49 public void configureSystemUnderTest() {
50 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
51 .build();
52 }
53
54 private ResultActions submitUpdateTaskForm() throws Exception {
55 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
56 .param("description", NEW_DESCRIPTION)
57 .param("id", Tasks.WriteLesson.ID.toString())
58 .param("title", NEW_TITLE)
59 );
60 }
61}
There are three 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.
- 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 nine integration tests for the processUpdateTaskForm() method.
First, we have to verify that it returns the HTTP status code 302. After we have written this integration test, 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.Test;
6import org.junit.experimental.categories.Category;
7import org.junit.runner.RunWith;
8import org.springframework.beans.factory.annotation.Autowired;
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.post;
23import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
24
25@RunWith(SpringJUnit4ClassRunner.class)
26@ContextConfiguration(classes = {IntegrationTestContext.class})
27@WebAppConfiguration
28@TestExecutionListeners({
29 DependencyInjectionTestExecutionListener.class,
30 TransactionalTestExecutionListener.class,
31 DbUnitTestExecutionListener.class,
32 ServletTestExecutionListener.class
33})
34@DatabaseSetup("task.xml")
35@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
36@Category(IntegrationTest.class)
37@ActiveProfiles(Profiles.INTEGRATION_TEST)
38public class ProcessUpdateTaskFormWhenValidationIsSuccessfulTest {
39
40 private static final String FEEDBACK_MESSAGE_TASK_UPDATED = "The information of a task was updated successfully.";
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 shouldReturnHttpStatusCodeFound() throws Exception {
58 submitUpdateTaskForm()
59 .andExpect(status().isFound());
60 }
61
62 private ResultActions submitUpdateTaskForm() throws Exception {
63 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
64 .param("description", NEW_DESCRIPTION)
65 .param("id", Tasks.WriteLesson.ID.toString())
66 .param("title", NEW_TITLE)
67 );
68 }
69}
Second, we have to ensure that it redirects the user to the view task view. 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.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.hamcrest.Matchers.is;
23import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
24import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
25import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
26import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
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 ProcessUpdateTaskFormWhenValidationIsSuccessfulTest {
42
43 private static final String FEEDBACK_MESSAGE_TASK_UPDATED = "The information of a task was updated successfully.";
44
45 private static final String NEW_DESCRIPTION = "The old lesson was not good";
46 private static final String NEW_TITLE = "Rewrite an existing lesson";
47
48 @Autowired
49 private WebApplicationContext webAppContext;
50
51 private MockMvc mockMvc;
52
53 @Before
54 public void configureSystemUnderTest() {
55 mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext)
56 .build();
57 }
58
59 @Test
60 public void shouldRedirectUserToViewTaskView() throws Exception {
61 submitUpdateTaskForm()
62 .andExpect(view().name("redirect:/task/{taskId}"))
63 .andExpect(model().attribute("taskId",
64 is(Tasks.WriteLesson.ID.toString())
65 ));
66 }
67
68 private ResultActions submitUpdateTaskForm() throws Exception {
69 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
70 .param("description", NEW_DESCRIPTION)
71 .param("id", Tasks.WriteLesson.ID.toString())
72 .param("title", NEW_TITLE)
73 );
74 }
75}
Third, we have to ensure that it redirects the user to view task page. After 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.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.post;
23import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
24
25@RunWith(SpringJUnit4ClassRunner.class)
26@ContextConfiguration(classes = {IntegrationTestContext.class})
27@WebAppConfiguration
28@TestExecutionListeners({
29 DependencyInjectionTestExecutionListener.class,
30 TransactionalTestExecutionListener.class,
31 DbUnitTestExecutionListener.class,
32 ServletTestExecutionListener.class
33})
34@DatabaseSetup("task.xml")
35@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
36@Category(IntegrationTest.class)
37@ActiveProfiles(Profiles.INTEGRATION_TEST)
38public class ProcessUpdateTaskFormWhenValidationIsSuccessfulTest {
39
40 private static final String FEEDBACK_MESSAGE_TASK_UPDATED = "The information of a task was updated successfully.";
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 shouldRedirectUserToViewTaskPageUrl() throws Exception {
58 submitUpdateTaskForm()
59 .andExpect(redirectedUrl("/task/2"));
60 }
61
62 private ResultActions submitUpdateTaskForm() throws Exception {
63 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
64 .param("description", NEW_DESCRIPTION)
65 .param("id", Tasks.WriteLesson.ID.toString())
66 .param("title", NEW_TITLE)
67 );
68 }
69}
Fourth, we have to ensure that it creates the feedback message that is shown to the user when the view task view is rendered. 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.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.post;
23import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
24
25@RunWith(SpringJUnit4ClassRunner.class)
26@ContextConfiguration(classes = {IntegrationTestContext.class})
27@WebAppConfiguration
28@TestExecutionListeners({
29 DependencyInjectionTestExecutionListener.class,
30 TransactionalTestExecutionListener.class,
31 DbUnitTestExecutionListener.class,
32 ServletTestExecutionListener.class
33})
34@DatabaseSetup("task.xml")
35@DbUnitConfiguration(dataSetLoader = ReplacementDataSetLoader.class)
36@Category(IntegrationTest.class)
37@ActiveProfiles(Profiles.INTEGRATION_TEST)
38public class ProcessUpdateTaskFormWhenValidationIsSuccessfulTest {
39
40 private static final String FEEDBACK_MESSAGE_TASK_UPDATED = "The information of a task was updated successfully.";
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 shouldAddFeedbackMessageAsAFlashAttribute() throws Exception {
58 submitUpdateTaskForm()
59 .andExpect(flash().attribute("feedbackMessage",
60 FEEDBACK_MESSAGE_TASK_UPDATED
61 ));
62 }
63
64 private ResultActions submitUpdateTaskForm() throws Exception {
65 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
66 .param("description", NEW_DESCRIPTION)
67 .param("id", Tasks.WriteLesson.ID.toString())
68 .param("title", NEW_TITLE)
69 );
70 }
71}
Fifth, we have to verify that it updates the title and description of the found 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.test.context.ActiveProfiles;
12import org.springframework.test.context.ContextConfiguration;
13import org.springframework.test.context.TestExecutionListeners;
14import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
15import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
16import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
17import org.springframework.test.context.web.ServletTestExecutionListener;
18import org.springframework.test.context.web.WebAppConfiguration;
19import org.springframework.test.web.servlet.MockMvc;
20import org.springframework.test.web.servlet.ResultActions;
21import org.springframework.test.web.servlet.setup.MockMvcBuilders;
22import org.springframework.web.context.WebApplicationContext;
23
24import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
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 ProcessUpdateTaskFormWhenValidationIsSuccessfulTest {
40
41 private static final String FEEDBACK_MESSAGE_TASK_UPDATED = "The information of a task was updated successfully.";
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 @ExpectedDatabase(value = "update-task-should-update-title-and-description.xml",
59 assertionMode = DatabaseAssertionMode.NON_STRICT
60 )
61 public void shouldUpdateTitleAndDescription() throws Exception {
62 submitUpdateTaskForm();
63 }
64
65 private ResultActions submitUpdateTaskForm() throws Exception {
66 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
67 .param(WebTestConstants.ModelAttributeProperty.Task.DESCRIPTION, NEW_DESCRIPTION)
68 .param(WebTestConstants.ModelAttributeProperty.Task.ID, Tasks.WriteLesson.ID.toString())
69 .param(WebTestConstants.ModelAttributeProperty.Task.TITLE, NEW_TITLE)
70 );
71 }
72}
The source code of the used 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>
Sixth, 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.test.context.ActiveProfiles;
12import org.springframework.test.context.ContextConfiguration;
13import org.springframework.test.context.TestExecutionListeners;
14import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
15import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
16import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
17import org.springframework.test.context.web.ServletTestExecutionListener;
18import org.springframework.test.context.web.WebAppConfiguration;
19import org.springframework.test.web.servlet.MockMvc;
20import org.springframework.test.web.servlet.ResultActions;
21import org.springframework.test.web.servlet.setup.MockMvcBuilders;
22import org.springframework.web.context.WebApplicationContext;
23
24import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
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 ProcessUpdateTaskFormWhenValidationIsSuccessfulTest {
40
41 private static final String FEEDBACK_MESSAGE_TASK_UPDATED = "The information of a task was updated successfully.";
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 @ExpectedDatabase(value = "update-task-should-update-lifecycle-fields.xml",
59 assertionMode = DatabaseAssertionMode.NON_STRICT
60 )
61 public void shouldUpdateModificationTimeAndVersion() throws Exception {
62 submitUpdateTaskForm();
63 }
64
65 private ResultActions submitUpdateTaskForm() throws Exception {
66 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
67 .param("description", NEW_DESCRIPTION)
68 .param("id", Tasks.WriteLesson.ID.toString())
69 .param("title", NEW_TITLE)
70 );
71 }
72}
The source code of the used 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>
Seventh, we have to ensure that it doesn’t change the status of the updated task. After we have written this test method 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.test.context.ActiveProfiles;
12import org.springframework.test.context.ContextConfiguration;
13import org.springframework.test.context.TestExecutionListeners;
14import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
15import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
16import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
17import org.springframework.test.context.web.ServletTestExecutionListener;
18import org.springframework.test.context.web.WebAppConfiguration;
19import org.springframework.test.web.servlet.MockMvc;
20import org.springframework.test.web.servlet.ResultActions;
21import org.springframework.test.web.servlet.setup.MockMvcBuilders;
22import org.springframework.web.context.WebApplicationContext;
23
24import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
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 ProcessUpdateTaskFormWhenValidationIsSuccessfulTest {
40
41 private static final String FEEDBACK_MESSAGE_TASK_UPDATED = "The information of a task was updated successfully.";
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 @ExpectedDatabase(value = "update-task-should-not-change-status.xml",
59 assertionMode = DatabaseAssertionMode.NON_STRICT
60 )
61 public void shouldNotChangeStatus() throws Exception {
62 submitUpdateTaskForm();
63 }
64
65 private ResultActions submitUpdateTaskForm() throws Exception {
66 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
67 .param("description", NEW_DESCRIPTION)
68 .param("id", Tasks.WriteLesson.ID.toString())
69 .param("title", NEW_TITLE)
70 );
71 }
72}
The source code of the used 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>
Eighth, we have to verify that it doesn’t change the assignee 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.test.context.ActiveProfiles;
12import org.springframework.test.context.ContextConfiguration;
13import org.springframework.test.context.TestExecutionListeners;
14import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
15import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
16import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
17import org.springframework.test.context.web.ServletTestExecutionListener;
18import org.springframework.test.context.web.WebAppConfiguration;
19import org.springframework.test.web.servlet.MockMvc;
20import org.springframework.test.web.servlet.ResultActions;
21import org.springframework.test.web.servlet.setup.MockMvcBuilders;
22import org.springframework.web.context.WebApplicationContext;
23
24import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
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 ProcessUpdateTaskFormWhenValidationIsSuccessfulTest {
40
41 private static final String FEEDBACK_MESSAGE_TASK_UPDATED = "The information of a task was updated successfully.";
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 @ExpectedDatabase(value = "update-task-should-not-change-assignee.xml",
59 assertionMode = DatabaseAssertionMode.NON_STRICT
60 )
61 public void shouldNotChangeAssignee() throws Exception {
62 submitUpdateTaskForm();
63 }
64
65 private ResultActions submitUpdateTaskForm() throws Exception {
66 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
67 .param("description", NEW_DESCRIPTION)
68 .param("id", Tasks.WriteLesson.ID.toString())
69 .param("title", NEW_TITLE)
70 );
71 }
72}
The source code of the used 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>
Ninth, we have to verify that it doesn’t make any changes to the tags of updated task. After we have written 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.test.context.ActiveProfiles;
12import org.springframework.test.context.ContextConfiguration;
13import org.springframework.test.context.TestExecutionListeners;
14import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
15import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
16import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
17import org.springframework.test.context.web.ServletTestExecutionListener;
18import org.springframework.test.context.web.WebAppConfiguration;
19import org.springframework.test.web.servlet.MockMvc;
20import org.springframework.test.web.servlet.ResultActions;
21import org.springframework.test.web.servlet.setup.MockMvcBuilders;
22import org.springframework.web.context.WebApplicationContext;
23
24import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
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 ProcessUpdateTaskFormWhenValidationIsSuccessfulTest {
40
41 private static final String FEEDBACK_MESSAGE_TASK_UPDATED = "The information of a task was updated successfully.";
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 @ExpectedDatabase(value = "update-task-should-not-change-tags.xml",
59 assertionMode = DatabaseAssertionMode.NON_STRICT
60 )
61 public void shouldNotMakeAnyChangesToTagsOfUpdatedTask() throws Exception {
62 submitUpdateTaskForm();
63 }
64
65 private ResultActions submitUpdateTaskForm() throws Exception {
66 return mockMvc.perform(post("/task/{taskId}/update", Tasks.WriteLesson.ID)
67 .param("description", NEW_DESCRIPTION)
68 .param("id", Tasks.WriteLesson.ID.toString())
69 .param("title", NEW_TITLE)
70 );
71 }
72}
The source code of the used DbUnit data set 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 processUpdateTaskForm() method is working as expected. Let’s summarize what we learned from this lesson.
Summary
This lesson has taught us four things:
- DbUnit doesn’t have an assertion mode that allows us to ignore the row count of the database tables that are found from our data sets.
- If we are writing integration tests for a method that inserts new rows into our database tables, we should use a DbUnit data set that doesn’t insert any rows into these tables.
- If we are writing integration tests for a method that updates existing rows of our database tables, we should use a DbUnit data set that inserts one row into the updated tables.
- When we write assertions for the data found from our database, we should use small data sets that assert one logical concept.