前一篇文章我们已经知道如何配置使用了 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/

如何为 SpringMVC 编写单元测试:普通 Controller 测试(转)的更多相关文章

  1. Google C++单元测试框架GoogleTest---GTest的Sample1和编写单元测试的步骤

    如果你还没有搭建gtest框架,可以参考我之前的博客:http://www.cnblogs.com/jycboy/p/6001153.html.. 1.The first sample: sample ...

  2. mvn编写主代码与测试代码

    maven编写主代码与测试代码 3.2 编写主代码 项目主代码和测试代码不同,项目的主代码会被打包到最终的构件中(比如jar),而测试代码只在运行测试时用到,不会被打包.默认情况下,Maven假设项目 ...

  3. 在Android Studio中进行单元测试和UI测试

    本篇教程翻译自Google I/O 2015中关于测试的codelab,掌握科学上网的同学请点击这里阅读:Unit and UI Testing in Android Studio.能力有限,如有翻译 ...

  4. ABP入门系列(11)——编写单元测试

    ABP入门系列目录--学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 1. 前言 In computer programming, unit testing is a ...

  5. JUnit编写单元测试代码注意点小结

    用eclipse编写单元测试的时候,可以直接选中某个类,然后右键new新疆一个junit case,界面如下图1所示: 图1:新建test case 选 择图1中的JUnit Test Case,然后 ...

  6. JUint4的下载、配置及对一个算法编写单元测试用例(测试多组数据每组多个参数)

    一.JUnit4 jar包下载 链接:https://pan.baidu.com/s/1AdeVGGikcY5dfL151ZnWHA 提取码:h1am 下载完成后,解压一下即可. 二.导入JUnit4 ...

  7. Android单元测试与模拟测试详解

    测试与基本规范 为什么需要测试? 为了稳定性,能够明确的了解是否正确的完成开发. 更加易于维护,能够在修改代码后保证功能不被破坏. 集成一些工具,规范开发规范,使得代码更加稳定( 如通过 phabri ...

  8. 实现如下类之间的继承关系,并编写Music类来测试这些类。

    实现如下类之间的继承关系,并编写Music类来测试这些类. package com.hanqi.test; public class Instrument { //输出弹奏乐器 public void ...

  9. Intellij Idea系列之导Jar包与编写单元测试(二)

     Intellij Idea系列之导Jar包与编写单元测试(二) 一.初衷 对于很多的初学者来说,Intellij如何导入jar包感到很迷惑,甚至在网上搜过相关文章之后还是云里雾里,本博客通过图文并茂 ...

随机推荐

  1. 1.介绍(introduction)

    这里主要记录一本书的学习过程: 条件独立: 意思是X和Y在given Z的情况下是独立的. 满足P(X,Y|Z) = P(X|Z)*P(Y|Z)以及P(X|Y,Z) = P(X|Z) 条件独立的一些性 ...

  2. Flume的监控参数

    参考 flume的http监控参数说明 普通的flume启动命令 bin/flume-ng agent -c conf -f conf/flume-conf.properties -n agent - ...

  3. 蜕变成蝶~Linux设备驱动中的并发控制

    并发和竞争发生在两类体系中: 对称多处理器(SMP)的多个CPU 内核可抢占的单CPU系统 访问共享资源的代码区域称为临界区(critical sections),临界区需要以某种互斥机制加以保护.在 ...

  4. 深入理解String类详解

    1.Stringstr = "eee" 和String str = new String("eee")的区别 先看一小段代码, 1 public static ...

  5. Oracle递归查询,Oracle START WITH……CONNECT BY查询

    Oracle递归查询,Oracle START WITH……CONNECT BY查询,Oracle树查询 ================================ ©Copyright 蕃薯耀 ...

  6. 树pao(雾)

    今天难得睡醒了,大概睡了13个小时,打算今晚把树剖学了. 嘿嘿嘿雀魂真好玩,这篇文章咕了 有学上了,找到了水平远高于我的队友,好开心的说,,, 卡空间感觉治不了了... 板子题是洛谷P3384 实在不 ...

  7. laravel安装一直报错

    laravel安装一直报错 原因: 1.找到php版本是否对应 2.缺少第三方扩展库vendor 需要composer update 解决链接:https://learnku.com/docs/lar ...

  8. eclipse主题之-------DevStyle

    最佳因为经常熬夜  eclipse自带的背景 白色 太伤眼了 换了其他颜色但是 总感觉 差点什么    所以就去eclipse自带的插件下载中心 下载了  DevStyle 其实 有很多的 主题插件的 ...

  9. mybatis14--注解的配置

    去掉对应的mapper映射文件 在dao文件中增加注解 public interface StudentDao { /** * 新增学生信息 */ @Insert(value="insert ...

  10. div加链接 html给div加超链接实现点击div跳转的方法[申明:来源于网络]

    div加链接 html给div加超链接实现点击div跳转的方法[申明:来源于网络] 地址:http://www.cdxwcx.com/faq/htmldivLink.html