前一篇文章我們已經知道如何配置使用了 SpringMVC 測試框架的單元測試。
現在我們就該親身實踐下如何為普通 Controller 編寫單元測試了。
接下來一個很明顯的問題就是:
什么是普通 Controller
其實,就這篇文章來說普通 Controller 就是指負責渲染界面或處理請求的 Controller。
如果你沒讀過前面的配置篇,那么我建議你先讀一下。
使用 Maven 獲取必須依賴
我們可以通過為我們的樣例程序中的 POM 文件添加以下依賴聲明來獲取必須依賴:
Jackson 2.2.1 (core 和 databind 模塊)。我們使用 Jackson 把對象轉化為字符串。
Hamcrest 1.3。使用它為返回內容寫斷言。
JUnit 4.11 (不需要包括 hamcrest-core 依賴).
Mockito 1.9.5
Spring Test 3.2.3.RELEASE
體現在 pom.xml 文件中的相關配置如下:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.2.1</version> <scope>test</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.2.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-all</artifactId> <version>1.3</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> <exclusions> <exclusion> <artifactId>hamcrest-core</artifactId> <groupId>org.hamcrest</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>1.9.5</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>3.2.3.RELEASE</version> <scope>test</scope> </dependency>
接下來讓我們看看如何使用 SpringMVC 測試框架為普通 Controller 寫單元測試。
為 Controller 中方法編寫單元測試
我們為 Controller 中方法寫的每一個測試都包括以下幾個步驟:
往 Controller 中方法發送一個請求。
驗證返回的內容是不是符合預期。
SpringMVC 為我們更便捷的實現這幾步提供了幾個核心類。基本描述如下:
我們可以使用 MockMvcRequestBuilders 類的靜態方法構建請求對象。更確切的說, 我們可以新建一個請求構建對象並把它當作參數傳遞給請求方法。
MockMvc 類是整個測試的入口。我們可以調用它的 perform(RequestBuilder requestBuilder) 方法執行請求。
我們可以使用 MockMvcResultMatchers 類的表態方法為接收到的返回對象寫斷言。
接下來讓我們看幾個如何在測試中使用它們的例子。我們會如下幾個方法編寫單元測試:
第一個 Controller 方法會為 Todo 對象渲染一個列表頁。
第二個 Controller 方法是為單個 Todo 對象渲染詳情頁。
第三個 Controller 方法接收表單並在數據庫中新建 Todo 對象記錄。
渲染 Todo 對象列表頁
讓我們先了解下渲染 Todo 對象列表頁的代碼。
預期行為
用來展示列表的方法實現主要包括以下幾步:
接收發送到 ‘/’ 地址的 GET 請求。
調用 TodoService 接口的 findAll() 方法獲取全部 Todo 對象。這個方法會返回一個包含 Todo 對象的列表。
把接收到的列表添加到 Model 對象中。
返回待渲染視圖名稱。
相關的 TodoController 類中代碼如下:
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import java.util.List; @Controller public class TodoController { private final TodoService service; @RequestMapping(value = "/", method = RequestMethod.GET) public String findAll(Model model) { List<Todo> models = service.findAll(); model.addAttribute("todos", models); return "todo/list"; } }
現在我們可以開始為這個方法寫單元測試了。
測試: 查詢到 Todo 對象列表時
我們可以通過以下幾步為這個 Controller 方法編寫單元測試:
創建 Service 中方法被調用時的返回數據。我們使用一個叫測試數據構建器的概念來表示創建測試數據。
將冒煙對象的 findAll() 方法被調用時的返回對象配置成前面創建的測試數據。
往 ‘/’ 地址發送一個 GET 請求。
確認返回的 HTTP 狀態碼是 200。
確認返回的視圖名稱是 ‘todo/list’。
確認請求被定向到地址 ‘/WEB-INF/jsp/todo/list.jsp’。
確認 Model 對象中的 todos 屬性中有兩個元素。
確認 Model 對象中的 todos 屬性中的對象都是正確的。
確認冒煙對象的 findAll() 方法僅被調用過一次。
確認冒煙對象的其它方法在測試過程中沒被調用過。
單元測試源代碼如下:
import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import java.util.Arrays; import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestContext.class, WebAppContext.class}) @WebAppConfiguration public class TodoControllerTest { private MockMvc mockMvc; @Autowired private TodoService todoServiceMock; //此處添加 WebApplicationContext 字段 //setUp() 方法 @Test public void findAll_ShouldAddTodoEntriesToModelAndRenderTodoListView() throws Exception { Todo first = new TodoBuilder() .id(1L) .description("Lorem ipsum") .title("Foo") .build(); Todo second = new TodoBuilder() .id(2L) .description("Lorem ipsum") .title("Bar") .build(); when(todoServiceMock.findAll()).thenReturn(Arrays.asList(first, second)); mockMvc.perform(get("/")) .andExpect(status().isOk()) .andExpect(view().name("todo/list")) .andExpect(forwardedUrl("/WEB-INF/jsp/todo/list.jsp")) .andExpect(model().attribute("todos", hasSize(2))) .andExpect(model().attribute("todos", hasItem( allOf( hasProperty("id", is(1L)), hasProperty("description", is("Lorem ipsum")), hasProperty("title", is("Foo")) ) ))) .andExpect(model().attribute("todos", hasItem( allOf( hasProperty("id", is(2L)), hasProperty("description", is("Lorem ipsum")), hasProperty("title", is("Bar")) ) ))); verify(todoServiceMock, times(1)).findAll(); verifyNoMoreInteractions(todoServiceMock); } }
渲染 Todo 對象詳情頁
在寫具體測試代碼前,讓我們先看看待測方法的實現。
預期行為
用以展示單個 Todo 對象信息的 Controller 方法實現中主要包含以下幾步:
接收發送到 ‘/todo/{id}’ 地址的 GET 請求。{id} 是一個用於標識被請求 Todo 對象主鍵的地址變量。
它通過調用 TodoService 接口的 findById() 方法獲取被請求的 Todo 對象,方法參數是被請求 Todo 對象的主鍵。這個方法會返回找到的 Todo 對象。如果沒找到對應對象,這個方法會拋出 TodoNotFoundException 異常。
它會把找到的 Todo 對象添加到 Model 對象中。
返回待渲染的視圖對象。
Controller 方法源代碼如下:
import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; @Controller public class TodoController { private final TodoService service; @RequestMapping(value = "/todo/{id}", method = RequestMethod.GET) public String findById(@PathVariable("id") Long id, Model model) throws TodoNotFoundException { Todo found = service.findById(id); model.addAttribute("todo", found); return "todo/view"; } }
下一個問題就是:
什么時候拋出 TodoNotFoundException 異常?
在本系列指南前面我們提到過,我們為 Controller 類拋出的異常創建過一個處理類。這個處理類的配置信息大體是這樣的:
@Bean public SimpleMappingExceptionResolver exceptionResolver() { SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver(); Properties exceptionMappings = new Properties(); exceptionMappings.put("net.petrikainulainen.spring.testmvc.todo.exception.TodoNotFoundException", "error/404"); exceptionMappings.put("java.lang.Exception", "error/error"); exceptionMappings.put("java.lang.RuntimeException", "error/error"); exceptionResolver.setExceptionMappings(exceptionMappings); Properties statusCodes = new Properties(); statusCodes.put("error/404", "404"); statusCodes.put("error/error", "500"); exceptionResolver.setStatusCodes(statusCodes); return exceptionResolver; }
正如我們看到的,如果拋出一個 TodoNotFoundException 異常,應用會返回 404 狀態碼並渲染 ‘error/404′ 視圖。
為這個 Controller 方法編寫單元測試時很明顯包括以下兩步:
必須有一個用例用來確保當找不到 Todo 對象時應用能正確執行。
必須有一個用例用來確保當 Todo 對象被找到時也能正確執行。
現在看看這個測試應該怎么寫。
測試 1: 查詢不到 Todo 對象時
首先,我們必須要確保我們的應用在找不到被查詢 Todo 對象時也能正常工作。我們可以通過以下幾步來進行測試:
配置冒煙對象,讓它在 findById() 方法以參數 1L 被調用時拋出 TodoNotFoundException 異常。
往 ‘/todo/1′ 地址發送一個 GET 請求。
確認返回的 HTTP 狀態碼是 404。
確保返回的視圖名稱是 ‘error/404′。
確保請求被定向到地址 ‘/WEB-INF/jsp/error/404.jsp’。
確認 TodoService 接口的 findById() 方法僅被調用過一次而且參數為 1L。
確認冒煙對象的其它方法在測試期間沒被調用過。
這個測試用例的代碼如下:
import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestContext.class, WebAppContext.class}) @WebAppConfiguration public class TodoControllerTest { private MockMvc mockMvc; @Autowired private TodoService todoServiceMock; //Add WebApplicationContext field here //The setUp() method is omitted. @Test public void findById_TodoEntryNotFound_ShouldRender404View() throws Exception { when(todoServiceMock.findById(1L)).thenThrow(new TodoNotFoundException("")); mockMvc.perform(get("/todo/{id}", 1L)) .andExpect(status().isNotFound()) .andExpect(view().name("error/404")) .andExpect(forwardedUrl("/WEB-INF/jsp/error/404.jsp")); verify(todoServiceMock, times(1)).findById(1L); verifyZeroInteractions(todoServiceMock); } }
測試 2: 查詢到 Todo 對象時
接上文,現在我們需要寫一個確保應用在能查詢到 Todo 對象時也能正確工作的測試用例。基本步驟如下:
創建 Service 中方法被調用時返回的 Todo 對象。同樣,我們還是通過測試數據構建器創建測試對象。
配置冒煙對象,讓它在 findById() 方法以 1L 參數被調用時返回前面創建的 Todo 對象。
往 ‘/todo/1′ 地址發送 GET 請求。
確認返回的 HTTP 狀態碼是 200。
確保返回的視圖名稱是 ‘todo/view’。
確保請求被定向到地址 ‘/WEB-INF/jsp/todo/view.jsp’。
確認 Model 對象中的 Todo 對象主鍵是 1L。
確認 Model 對象中的 Todo 對象描述字段值為 ‘Lorem ipsum’。
確認 Model 對象中的 Todo 對象名稱字段值為 ‘Foo’。
確保冒煙對象的 findById() 方法僅被調用過一次,而且參數為 1L。
確保冒煙對象中的其它方法在測試過程中沒被調用過。
測試用例代碼如下:
import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestContext.class, WebAppContext.class}) @WebAppConfiguration public class TodoControllerTest { private MockMvc mockMvc; @Autowired private TodoService todoServiceMock; //Add WebApplicationContext field here //The setUp() method is omitted. @Test public void findById_TodoEntryFound_ShouldAddTodoEntryToModelAndRenderViewTodoEntryView() throws Exception { Todo found = new TodoBuilder() .id(1L) .description("Lorem ipsum") .title("Foo") .build(); when(todoServiceMock.findById(1L)).thenReturn(found); mockMvc.perform(get("/todo/{id}", 1L)) .andExpect(status().isOk()) .andExpect(view().name("todo/view")) .andExpect(forwardedUrl("/WEB-INF/jsp/todo/view.jsp")) .andExpect(model().attribute("todo", hasProperty("id", is(1L)))) .andExpect(model().attribute("todo", hasProperty("description", is("Lorem ipsum")))) .andExpect(model().attribute("todo", hasProperty("title", is("Foo")))); verify(todoServiceMock, times(1)).findById(1L); verifyNoMoreInteractions(todoServiceMock); } }
處理表單並在數據庫中添加 Todo 記錄
首先,在編寫測試用例前還是看一下待測試 Controller 方法的預期行為。
預期行為
負責處理表單並入庫的 Controller 方法基本實現步驟如下:
接收發送到 ‘/todo/add’ 地址的 POST 請求。
校驗傳遞過來的 BingdingResult 參數是正確的,否則返回表單視圖名稱。
以表單對象為參數調用 TodoService 接口的 add() 方法進行 Todo 對象入庫。
創建返回信息並將返回信息作為參數傳遞給 RedirectAttributes 中。
把入庫的 Todo 對象主鍵添加到 RedirectAttributes 中。
把請求重定向到 Todo 對象詳情頁並渲染。
位於 TodoController 類中的相關代碼如下:
import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import javax.validation.Valid; import java.util.Locale; @Controller @SessionAttributes("todo") public class TodoController { private final TodoService service; private final MessageSource messageSource; @RequestMapping(value = "/todo/add", method = RequestMethod.POST) public String add(@Valid @ModelAttribute("todo") TodoDTO dto, BindingResult result, RedirectAttributes attributes) { if (result.hasErrors()) { return "todo/add"; } Todo added = service.add(dto); addFeedbackMessage(attributes, "feedback.message.todo.added", added.getTitle()); attributes.addAttribute("id", added.getId()); return createRedirectViewPath("todo/view"); } private void addFeedbackMessage(RedirectAttributes attributes, String messageCode, Object... messageParameters) { String localizedFeedbackMessage = getMessage(messageCode, messageParameters); attributes.addFlashAttribute("feedbackMessage", localizedFeedbackMessage); } private String getMessage(String messageCode, Object... messageParameters) { Locale current = LocaleContextHolder.getLocale(); return messageSource.getMessage(messageCode, messageParameters, current); } private String createRedirectViewPath(String requestMapping) { StringBuilder redirectViewPath = new StringBuilder(); redirectViewPath.append("redirect:"); redirectViewPath.append(requestMapping); return redirectViewPath.toString(); } }
我們可以看到,這個方法用了一個 TodoDTO 對象來表示表單對象。TodoDTO 類只是一個簡單的數據傳輸類,它的代碼大致如下:
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.NotEmpty; public class TodoDTO { private Long id; @Length(max = 500) private String description; @NotEmpty @Length(max = 100) private String title; //Constructor and other methods are omitted. }
這個類中聲明了如下一些校驗約束:
Todo 對象中的 title 屬性不能為空。
description 屬性的最大長度是 500 字符。
title 屬性的最大長度是 100 字符。
如果我們仔細思考下我們該為這個方法寫的測試,就會發現我們至少有以下幾點需要做的:
在參數校驗失敗時 Controller 方法需要能正常工作。
校驗通過時也能正常工作並正常入庫。
現在讓我們看看這個測試應該怎么寫。
測試 1: 校驗失敗時
首先,我們需要測一下校驗失敗時 Controller 方法也能正常工作。這個測試我們可以這么干:
創建 title 屬性包含 101 字符。
創建 description 屬性包含 501 字符。
通過以下幾步往 ‘/todo/add’ 地址發送 POST 請求:
把請求的 Content-Type 設置成 ‘application/x-www-form-urlencoded’。
把前面提到的 title 和 description 當作請求參數發送過去。
在 session 中設置一個 TodoDTO 對象。這是必須的因為我們的 Controller 有一個 @SessionAttributes 注解。
校驗返回的 HTTP 狀態碼是 200。
校驗返回的視圖名稱是 ‘todo/add’。
校驗請求被定向到地址 ‘/WEB-INF/jsp/todo/add.jsp’。
校驗 model 對象中有 title 和 description 參數錯誤的提示信息。
確保 model 中的 id 屬性是空。
確保 model 中的 description 屬性是正確的。
確保 model 中的 title 屬性是正確的。
確保冒煙對象中的方法在測試過程中沒被調用過。
這個測試的源代碼大體如下:
import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestContext.class, WebAppContext.class}) @WebAppConfiguration public class TodoControllerTest { private MockMvc mockMvc; @Autowired private TodoService todoServiceMock; //此處添加 WebApplicationContext 字段 //setUp() 方法 @Test public void add_DescriptionAndTitleAreTooLong_ShouldRenderFormViewAndReturnValidationErrorsForTitleAndDescription() throws Exception { String title = TestUtil.createStringWithLength(101); String description = TestUtil.createStringWithLength(501); mockMvc.perform(post("/todo/add") .contentType(MediaType.APPLICATION_FORM_URLENCODED) .param("description", description) .param("title", title) .sessionAttr("todo", new TodoDTO()) ) .andExpect(status().isOk()) .andExpect(view().name("todo/add")) .andExpect(forwardedUrl("/WEB-INF/jsp/todo/add.jsp")) .andExpect(model().attributeHasFieldErrors("todo", "title")) .andExpect(model().attributeHasFieldErrors("todo", "description")) .andExpect(model().attribute("todo", hasProperty("id", nullValue()))) .andExpect(model().attribute("todo", hasProperty("description", is(description)))) .andExpect(model().attribute("todo", hasProperty("title", is(title)))); verifyZeroInteractions(todoServiceMock); } }
我們的測試用例調用了 TestUtil 類的 createStringWithLength(int length) 方法。這個方法會按照給定的長度創建並返回字符串。
TestUtil 類的源代碼如下:
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Iterator; import java.util.Map; import java.util.Set; public class TestUtil { public static String createStringWithLength(int length) { StringBuilder builder = new StringBuilder(); for (int index = 0; index < length; index++) { builder.append("a"); } return builder.toString(); } }
測試 2: 校驗通過且正常入庫時
現在,讓我們測試可以正常入庫時 Controller 能否正常工作。這個測試可以分以下幾步:
創建一個 Todo 對象,它會在 TodoService 接口的 add() 方法被調用時返回。
配置冒煙對象讓它在 add() 方法按給定 TodoDTO 對象為參數調用時返回前面創建的 Todo 對象。
通過以下幾步往 ‘/todo/add’ 地址發送 POST 請求:
把請求的 Content-Type 設置成 ‘application/x-www-form-urlencoded’。
把 Todo 對象的 description 和 title 字段當做參數發送請求。
在 session 中添加一個 TodoDTO 對象。因為我們的 Controller 類上包含一個 @SessionAttributes 注解。
確認返回的 HTTP 狀態碼是 302。
確認返回的視圖名稱是 ‘redirect:todo/{id}’。
確認請求被定向到地址 ‘/todo/1’。
確認 model 中 id 屬性值為 ‘1’。
確認返回信息已正確設置。
確認冒煙對象的 add() 方法僅被調用過一次而且參數是 TodoDTO 對象。使用 ArgumentCaptor 為使用的參數做一個快照。
確認冒煙對象的其它方法在測試過程中沒被調用過。
確認 TodoDTO 對象的和字段值是正確的。
這個單元測試的源代碼如下:
import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {TestContext.class, WebAppContext.class}) @WebAppConfiguration public class TodoControllerTest { private MockMvc mockMvc; @Autowired private TodoService todoServiceMock; //此處添加 WebApplicationContext 字段 //setUp() 方法 @Test public void add_NewTodoEntry_ShouldAddTodoEntryAndRenderViewTodoEntryView() throws Exception { Todo added = new TodoBuilder() .id(1L) .description("description") .title("title") .build(); when(todoServiceMock.add(isA(TodoDTO.class))).thenReturn(added); mockMvc.perform(post("/todo/add") .contentType(MediaType.APPLICATION_FORM_URLENCODED) .param("description", "description") .param("title", "title") .sessionAttr("todo", new TodoDTO()) ) .andExpect(status().isMovedTemporarily()) .andExpect(view().name("redirect:todo/{id}")) .andExpect(redirectedUrl("/todo/1")) .andExpect(model().attribute("id", is("1"))) .andExpect(flash().attribute("feedbackMessage", is("Todo entry: title was added."))); ArgumentCaptor<TodoDTO> formObjectArgument = ArgumentCaptor.forClass(TodoDTO.class); verify(todoServiceMock, times(1)).add(formObjectArgument.capture()); verifyNoMoreInteractions(todoServiceMock); TodoDTO formObject = formObjectArgument.getValue(); assertThat(formObject.getDescription(), is("description")); assertNull(formObject.getId()); assertThat(formObject.getTitle(), is("title")); } }
總結
我們現在已經使用 SpringMVC 測試框架寫了好幾個普通 Controller 的測試用例了。通過這篇指南我們學會了:
如何為被測試 Controller 方法創建請求對象。
如何為被測試 Controller 方法的返回做斷言。
如何為渲染視圖的 Controller 寫單元測試。
如何為處理表單的 Controller 寫單元測試。
本系列指南接下來的部分將會告訴我們如何為 REST API 寫單元測試。
轉載注明出處:如何為 SpringMVC 編寫單元測試:普通 Controller 測試
原文鏈接:http://lzxz1234.github.io/junit/2014/07/10/Unit-Testing-of-Spring-MVC-Controllers-Normal-Controller/