如何为 SpringMVC 编写单元测试:普通 Controller 测试(转)
前一篇文章我们已经知道如何配置使用了 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 测试(转)的更多相关文章
- Google C++单元测试框架GoogleTest---GTest的Sample1和编写单元测试的步骤
如果你还没有搭建gtest框架,可以参考我之前的博客:http://www.cnblogs.com/jycboy/p/6001153.html.. 1.The first sample: sample ...
- mvn编写主代码与测试代码
maven编写主代码与测试代码 3.2 编写主代码 项目主代码和测试代码不同,项目的主代码会被打包到最终的构件中(比如jar),而测试代码只在运行测试时用到,不会被打包.默认情况下,Maven假设项目 ...
- 在Android Studio中进行单元测试和UI测试
本篇教程翻译自Google I/O 2015中关于测试的codelab,掌握科学上网的同学请点击这里阅读:Unit and UI Testing in Android Studio.能力有限,如有翻译 ...
- ABP入门系列(11)——编写单元测试
ABP入门系列目录--学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 1. 前言 In computer programming, unit testing is a ...
- JUnit编写单元测试代码注意点小结
用eclipse编写单元测试的时候,可以直接选中某个类,然后右键new新疆一个junit case,界面如下图1所示: 图1:新建test case 选 择图1中的JUnit Test Case,然后 ...
- JUint4的下载、配置及对一个算法编写单元测试用例(测试多组数据每组多个参数)
一.JUnit4 jar包下载 链接:https://pan.baidu.com/s/1AdeVGGikcY5dfL151ZnWHA 提取码:h1am 下载完成后,解压一下即可. 二.导入JUnit4 ...
- Android单元测试与模拟测试详解
测试与基本规范 为什么需要测试? 为了稳定性,能够明确的了解是否正确的完成开发. 更加易于维护,能够在修改代码后保证功能不被破坏. 集成一些工具,规范开发规范,使得代码更加稳定( 如通过 phabri ...
- 实现如下类之间的继承关系,并编写Music类来测试这些类。
实现如下类之间的继承关系,并编写Music类来测试这些类. package com.hanqi.test; public class Instrument { //输出弹奏乐器 public void ...
- Intellij Idea系列之导Jar包与编写单元测试(二)
Intellij Idea系列之导Jar包与编写单元测试(二) 一.初衷 对于很多的初学者来说,Intellij如何导入jar包感到很迷惑,甚至在网上搜过相关文章之后还是云里雾里,本博客通过图文并茂 ...
随机推荐
- 1.介绍(introduction)
这里主要记录一本书的学习过程: 条件独立: 意思是X和Y在given Z的情况下是独立的. 满足P(X,Y|Z) = P(X|Z)*P(Y|Z)以及P(X|Y,Z) = P(X|Z) 条件独立的一些性 ...
- Flume的监控参数
参考 flume的http监控参数说明 普通的flume启动命令 bin/flume-ng agent -c conf -f conf/flume-conf.properties -n agent - ...
- 蜕变成蝶~Linux设备驱动中的并发控制
并发和竞争发生在两类体系中: 对称多处理器(SMP)的多个CPU 内核可抢占的单CPU系统 访问共享资源的代码区域称为临界区(critical sections),临界区需要以某种互斥机制加以保护.在 ...
- 深入理解String类详解
1.Stringstr = "eee" 和String str = new String("eee")的区别 先看一小段代码, 1 public static ...
- Oracle递归查询,Oracle START WITH……CONNECT BY查询
Oracle递归查询,Oracle START WITH……CONNECT BY查询,Oracle树查询 ================================ ©Copyright 蕃薯耀 ...
- 树pao(雾)
今天难得睡醒了,大概睡了13个小时,打算今晚把树剖学了. 嘿嘿嘿雀魂真好玩,这篇文章咕了 有学上了,找到了水平远高于我的队友,好开心的说,,, 卡空间感觉治不了了... 板子题是洛谷P3384 实在不 ...
- laravel安装一直报错
laravel安装一直报错 原因: 1.找到php版本是否对应 2.缺少第三方扩展库vendor 需要composer update 解决链接:https://learnku.com/docs/lar ...
- eclipse主题之-------DevStyle
最佳因为经常熬夜 eclipse自带的背景 白色 太伤眼了 换了其他颜色但是 总感觉 差点什么 所以就去eclipse自带的插件下载中心 下载了 DevStyle 其实 有很多的 主题插件的 ...
- mybatis14--注解的配置
去掉对应的mapper映射文件 在dao文件中增加注解 public interface StudentDao { /** * 新增学生信息 */ @Insert(value="insert ...
- div加链接 html给div加超链接实现点击div跳转的方法[申明:来源于网络]
div加链接 html给div加超链接实现点击div跳转的方法[申明:来源于网络] 地址:http://www.cdxwcx.com/faq/htmldivLink.html