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.

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:

  1. If the submitted form has validation errors, return the name of the form view (’task/update’).
  2. Update the information of the task.
  3. 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.
  4. 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:

  1. 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.
  2. Create a private method that submits an empty update task form. This method returns a ResultActions object, 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}
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 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}
If we are not using JSP views, we don’t have to write this integration test because our controller can render the requested view without forwarding the user to the view page. Instead, it will read the content of the view from the template file and return the content of the rendered view in the response body.

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:

  1. 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.
  2. Declare the constant that contains the expected feedback message that is read from the localization file.
  3. Declare the constants that contain the new title and description of the updated task.
  4. 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>
As you can see, the idea behind the last five integration tests is to write our assertions by using DbUnit data sets that assert one logical concept. These data sets are easy to read, and they help us to write integration tests that emphasize the requirements of the tested controller methods.

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.

Get the source code from Github