Spring MVC控制器的单元测试:“普通”控制器

本教程的第一部分描述了如何配置使用Spring MVC Test框架的单元测试 。 现在是时候动手做,学习如何为“常规”控制器编写单元测试了。

显而易见的下一个问题是:

什么是普通控制器?

好吧,一个普通的控制器(在此博客文章的上下文中)是一个控制器,它要么呈现视图,要么处理表单提交。

让我们开始吧。

注意 :建议您在阅读此博客文章之前先阅读本教程的第一部分 (如果已经阅读过,可以继续阅读)

使用Maven获取所需的依赖关系

通过将以下依赖项声明添加到示例应用程序的POM文件中,我们可以获得所需的测试依赖项:

  • 杰克逊2.2.1(核心和数据绑定模块)。 我们使用Jackson将对象转换为url编码的String对象。
  • Hamcrest 1.3。 在为响应编写断言时,我们使用Hamcrest匹配器。
  • JUnit 4.11(不包括hamcrest-core依赖性)。
  • Mockito 1.9.5
  • Spring测试3.2.3发布

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>

让我们继续研究如何使用Spring MVC Test框架为Spring MVC控制器编写单元测试。

编写控制器方法的单元测试

我们编写的用于测试控制器方法行为的每个单元测试均包含以下步骤:

  1. 我们向已测试的控制器方法发送请求。
  2. 我们确认我们收到了预期的答复。

Spring MVC测试框架具有一些“核心”类,我们可以使用这些类在测试中实现这些步骤。 这些类的描述如下:

  • 我们可以使用MockMvcRequestBuilders类的静态方法来构建请求。 更具体地说,我们可以创建请求构建器 ,然后将其作为方法参数传递给执行实际请求的方法。
  • MockMvc类是测试的主要入口点。 我们可以通过调用其perform(RequestBuilder requestBuilder)方法来执行请求。
  • 我们可以使用MockMvcResultMathers类的静态方法为收到的响应编写断言。

接下来,我们将看一些示例,这些示例演示了如何在单元测试中使用这些类。 我们将为以下控制器方法编写单元测试:

  • 第一个控制器方法呈现一个页面,该页面显示待办事项列表。
  • 第二种控制器方法呈现一个页面,该页面显示单个待办事项的信息。
  • 第三种控制器方法处理表单的表单提交,该表单用于向数据库添加新的待办事项。

渲染Todo条目列表页面

让我们首先看一下用于呈现待办事项条目列表页面的controller方法的实现。

预期行为

用于显示所有待办事项信息的控制器方法的实现包括以下步骤:

  1. 它通过调用TodoService接口的findAll()方法来获取待办事项。 此方法返回Todo对象的列表。
  2. 它将接收到的列表添加到模型。
  3. 它返回渲染视图的名称。

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";}
}

现在,我们准备为此方法编写单元测试。 让我们看看我们如何做到这一点。

测试:找到待办事项

我们可以通过以下步骤为此控制器方法编写单元测试:

  1. 创建在调用我们的服务方法时返回的测试数据。 在为测试创建测试数据时,我们使用称为测试数据生成器的概念。
  2. 配置使用的模拟对象以在调用其findAll()方法时返回创建的测试数据。
  3. 执行GET请求以发送URL'/'。
  4. 确保返回HTTP状态代码200。
  5. 确保返回的视图的名称为“ todo / list”。
  6. 确保请求转发到URL'/WEB-INF/jsp/todo/list.jsp'。
  7. 确保名为todos的模型属性包含两个项目。
  8. 确保名为todos的模型属性包含正确的项目。
  9. 验证模拟对象的findAll()方法仅被调用一次。
  10. 确保在测试过程中未调用模拟对象的其他方法。

我们的单元测试的源代码如下所示:

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;@Autowiredprivate TodoService todoServiceMock;//Add WebApplicationContext field here//The setUp() method is omitted.@Testpublic 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);}
}

渲染View Todo输入页面

在为控制器方法编写实际的单元测试之前,我们必须仔细研究该方法的实现。 让我们继续前进,了解控制器的实现方式。

预期行为

通过执行以下步骤来实现用于显示单个待办事项信息的控制器方法:

  1. 它通过调用TodoService接口的findById()方法获取请求的待办事项条目,并将请求的待办事项条目的ID作为方法参数传递。 此方法返回找到的待办事项条目。 如果未找到待办事项条目,则此方法抛出TodoNotEntryNotFoundException
  2. 它将找到的待办事项条目添加到模型中。
  3. 它返回渲染视图的名称。

我们的控制器方法的源代码如下所示:

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";}
}

我们的下一个问题是:

抛出TodoEntryNotFoundException会发生什么?

在本教程的上一部分中,我们创建了一个异常解析器bean,用于处理控制器类抛出的异常。 该bean的配置如下所示:

@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;
}

如我们所见,如果抛出TodoEntryNotFoundException ,我们的应用程序将呈现“错误/ 404”视图并返回HTTP状态代码404。

显然,我们必须为此控制器方法编写两个测试:

  1. 我们必须编写一个测试,以确保在未找到todo条目时,我们的应用程序能够正常运行。
  2. 找到待办事项后,我们必须编写一个测试来验证我们的应用程序是否正常运行。

让我们看看如何编写这些测试。

测试1:找不到待办事项条目

首先,当找不到请求的待办事项条目时,我们必须确保我们的应用程序处于工作状态。 我们可以按照以下步骤编写测试来确保这一点:

  1. 将模拟对象配置为在调用findById()方法且请求的待办事项条目的ID为1L时引发TodoNotFoundException
  2. 执行GET请求以发送url'/ todo / 1'。
  3. 验证是否返回了HTTP状态代码404。
  4. 确保返回的视图的名称为“ error / 404”。
  5. 确保将请求转发到URL“ /WEB-INF/jsp/error/404.jsp”。
  6. 验证仅使用正确的方法参数(1L)调用一次TodoService接口的findById()方法。
  7. 验证在此测试期间没有调用模拟对象的其他方法。

我们的单元测试的源代码如下所示:

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;@Autowiredprivate TodoService todoServiceMock;//Add WebApplicationContext field here//The setUp() method is omitted.@Testpublic 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条目

其次,我们必须编写一个测试,以确保找到待办事项时控制器能够正常工作。 我们可以按照以下步骤进行操作:

  1. 创建Todo对象,该对象在调用我们的service方法时返回。 同样,我们使用测试数据生成器创建返回的Todo对象。
  2. 配置我们的模拟对象以在使用方法参数1L调用其findById()方法时返回创建的Todo对象。
  3. 执行GET请求以发送url'/ todo / 1'。
  4. 验证是否返回HTTP状态代码200。
  5. 确保返回的视图名称为“ todo / view”。
  6. 确保将请求转发到URL“ /WEB-INF/jsp/todo/view.jsp”。
  7. 验证名为todo的模型对象的ID为1L。
  8. 验证名为todo的模型对象的描述为“ Lorem ipsum”。
  9. 验证名为todo的模型对象的标题为“ Foo”。
  10. 确保使用正确的方法参数(1L)仅对模拟对象的findById()方法进行一次调用。
  11. 确保在测试期间未调用模拟对象的其他方法。

我们的单元测试的源代码如下所示:

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;@Autowiredprivate TodoService todoServiceMock;//Add WebApplicationContext field here//The setUp() method is omitted.@Testpublic 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);}
}

处理“添加待办事项”输入表单的表单提交

同样,我们将首先查看控制器方法的预期行为,然后再为其编写单元测试。

预期行为

通过执行以下步骤来实现处理添加待办事项条目表单的表单提交的controller方法:

  1. 它检查作为方法参数给出的BindingResult对象是否没有任何错误。 如果发现错误,它将返回表单视图的名称。
  2. 它通过调用TodoService接口的add()方法添加一个新的Todo条目,并将form对象作为方法参数传递。 此方法创建一个新的待办事项条目并返回它。
  3. 它创建有关添加的待办事项条目的反馈消息,并将该消息添加到作为方法参数给出的RedirectAttributes对象。
  4. 它将添加的待办事项条目的ID添加到RedirectAttributes对象。
  5. 它返回重定向视图的名称,该名称将请求重定向到视图待办事项输入页面。

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类是一个简单的DTO类,其源代码如下所示:

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.
}

TodoDTO类声明一些验证约束,这些约束如下所述:

  • 待办事项的标题不能为空。
  • 说明的最大长度为500个字符。
  • 标题的最大长度为100个字符。

如果考虑为该控制器方法编写的测试,则很明显,我们必须确保

  1. 验证失败时,控制器方法为工作属性。
  2. 当待办事项条目添加到数据库时,控制器方法是工作属性。

让我们找出如何编写这些测试。

测试1:验证失败

首先,我们必须编写一个测试,以确保验证失败时我们的控制器方法能够正常工作。 我们可以按照以下步骤编写此测试:

  1. 创建一个具有101个字符的标题
  2. 创建一个包含501个字符的描述
  3. 使用我们的测试数据构建器创建一个新的TodoDTO对象。 设置对象的标题描述
  4. 执行POST请求以发送网址“ todo / add”。 将请求的内容类型设置为“ application / x-www-form-urlencoded”。 确保我们的表单对象的内容在请求的正文中发送。 将表单对象设置为会话。
  5. 验证是否返回HTTP状态代码200。
  6. 确认返回的视图名称为“ todo / add”。
  7. 验证请求是否转发到URL'/WEB-INF/jsp/todo/add.jsp'。
  8. 验证我们的模型属性在标题描述字段中是否存在字段错误。
  9. 确保我们的model属性的id为null。
  10. 确保对我们的模型属性的描述正确。
  11. 确保模型属性的标题正确。
  12. 确保在测试过程中未调用模拟对象的方法。

我们的单元测试的源代码如下所示:

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;@Autowiredprivate TodoService todoServiceMock;//Add WebApplicationContext field here//The setUp() method is omitted.@Testpublic void add_DescriptionAndTitleAreTooLong_ShouldRenderFormViewAndReturnValidationErrorsForTitleAndDescription() throws Exception {String title = TestUtil.createStringWithLength(101);String description = TestUtil.createStringWithLength(501);TodoDTO formObject =  new TodoDTOBuilder().description(description).title(title).build();mockMvc.perform(post("/todo/add").contentType(MediaType.APPLICATION_FORM_URLENCODED).content(TestUtil.convertObjectToFormUrlEncodedBytes(formObject)).sessionAttr("todo", formObject)).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)方法使用给定的长度创建一个新的String对象,并返回创建的对象。
  • 所述convertObjectToFormUrlEncodedBytes(Object对象)方法转换对象变成形式URL编码字符串对象,并返回字符串对象的内容作为一个字节数组。

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 byte[] convertObjectToFormUrlEncodedBytes(Object object) {ObjectMapper mapper = new ObjectMapper();mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);Map<String, Object> propertyValues = mapper.convertValue(object, Map.class);Set<String> propertyNames = propertyValues.keySet();Iterator<String> nameIter = propertyNames.iterator();StringBuilder formUrlEncoded = new StringBuilder();for (int index=0; index < propertyNames.size(); index++) {String currentKey = nameIter.next();Object currentValue = propertyValues.get(currentKey);formUrlEncoded.append(currentKey);formUrlEncoded.append("=");formUrlEncoded.append(currentValue);if (nameIter.hasNext()) {formUrlEncoded.append("&");}}return formUrlEncoded.toString().getBytes();}public static String createStringWithLength(int length) {StringBuilder builder = new StringBuilder();for (int index = 0; index < length; index++) {builder.append("a");}return builder.toString();}
}

测试2:Todo条目已添加到数据库

其次,我们必须编写一个测试,以确保在将新的待办事项添加到数据库时,控制器能够正常工作。 我们可以按照以下步骤编写此测试:

  1. 通过使用测试数据构建器类创建一个表单对象。 将“ legal”值设置为所创建对象的标题描述字段。
  2. 创建一个Todo对象,该对象在调用TodoService接口的add()方法时返回。
  3. 配置我们的模拟对象,使其在调用其add()方法并将创建的表单对象作为方法参数给出时返回创建的Todo对象。
  4. 执行POST请求以发送网址“ todo / add”。 将请求的内容类型设置为“ application / x-www-form-urlencoded”。 确保我们的表单对象的内容在请求的正文中发送。 将表单对象设置为会话。
  5. 验证是否返回了HTTP状态代码302。
  6. 确保返回的视图的名称为'redirect:todo / {id}'。
  7. 确保将请求重定向到URL'/ todo / 1'。
  8. 验证名为id的模型属性为“ 1”。
  9. 验证是否设置了反馈消息。
  10. 验证我们的模拟对象的add()方法仅被调用一次,并且表单对象作为方法参数给出。
  11. 验证在测试过程中没有调用过模拟对象的其他方法。

我们的单元测试的源代码如下所示:

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.is;
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;@Autowiredprivate TodoService todoServiceMock;//Add WebApplicationContext field here//The setUp() method is omitted.@Testpublic void add_NewTodoEntry_ShouldAddTodoEntryAndRenderViewTodoEntryView() throws Exception {TodoDTO formObject = new TodoDTOBuilder().description("description").title("title").build();Todo added = new TodoBuilder().id(1L).description(formObject.getDescription()).title(formObject.getTitle()).build();when(todoServiceMock.add(formObject)).thenReturn(added);mockMvc.perform(post("/todo/add").contentType(MediaType.APPLICATION_FORM_URLENCODED).content(TestUtil.convertObjectToFormUrlEncodedBytes(formObject)).sessionAttr("todo", formObject)).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.")));verify(todoServiceMock, times(1)).add(formObject);verifyNoMoreInteractions(todoServiceMock);}
}

摘要

现在,我们使用Spring MVC Test框架为“常规”控制器方法编写了一些单元测试。 本教程讲授了四件事:

  • 我们学会了创建请求,这些请求由经过测试的控制器方法处理。
  • 我们学会了为已测试的控制器方法返回的响应编写断言。
  • 我们学习了如何为呈现视图的控制器方法编写单元测试。
  • 我们学会了为处理表单提交的控制器方法编写单元测试。

本教程的下一部分描述了如何为REST API编写单元测试。

PS此博客文章的示例应用程序可在Github上获得 。 我建议您检查一下,因为它有一些单元测试,而本博客文章中未涉及。

参考: Spring MVC控制器的单元测试: Petri Kainulainen博客上来自我们JCG合作伙伴 Petri Kainulainen的“常规”控制器 。

翻译自: https://www.javacodegeeks.com/2013/07/unit-testing-of-spring-mvc-controllers-normal-controllers.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367927.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

回溯法

一、概念&#xff1a;回溯法也是一种枚举&#xff0c;但是回溯法将枚举(生成&#xff09;和检查有机结合起来&#xff0c;从而减少了不必要的枚举。。 二、经典八皇后问题。 #include<iostream> #include<cmath> using namespace std;int vis[100]; int store[100]…

【Spring】入门HelloWorld

参考&#xff1a;https://www.yiibai.com/spring/spring-tutorial-for-beginners.html 一、创建项目 1.利用IntelliJ创建Maven项目2.配置pom.xml,引入Spring <?xml version"1.0" encoding"UTF-8"?> 4.0.0 <groupId>com.jh</groupId> &…

html属性可以用来定义内联样式,18年6月考试《网页设计与制作》期末大作业.doc...

...(单选题) 1: 以下HTML代码&#xff0c;判断正确的是( )。 A: 错了B: “button”双引号错了(应为单引号)C: 第2、3行应加入…….D: 没有错误正确答案:(单选题) 2: 数据库的基本操作是( )。A: 增&#xff0c;删&#xff0c;改&#xff0c;查B: 增&#xff0c;删&#xff0c;加…

DIV CSS初学者需重视的10个简单问题与技巧

DIVCSS初学者往往感觉自己遇到很多莫明其妙的问题&#xff0c;其实只是一些小细节没有引起重视&#xff0c;下面罗列了10个问题与技巧&#xff0c;温故而知新。 一、检查HTML元素是否有拼写错误、是否忘记结束标记即使是老手也经常会弄错div的嵌套关系。可以用dreamweaver的验…

Spring MVC:表单处理卷。 5 –选择,选项,选项标签

下拉列表是Web表单中最常见的元素之一。 在HTML中&#xff0c;可以使用适当的标签创建此类控件&#xff1a; <form&#xff1a;select> –下拉列表的父标签和<form&#xff1a;option> – <form&#xff1a;select>标签的子标签。 Spring MVC标签库为下拉列表…

06.linux平台下运行go文件

1.获取linux位数 getconf LONG_BIT 2.下载地址&#xff1a; https://studygolang.com/dl 3.解压安装包 sudo tar -xzf go1.9.2.linux-amd64.tar.gz -C /usr/local 4.设置环境变量 sudo vi /etc/profile export GOROOT/usr/local/go export GOBIN$GOROOT/bin export GOPATH/h…

python3读取ini文件_python3配置文件ini读取方法

1.最常用的Plain text形式的配置文件 1.1首选 .init 文件 标准库 configparser 1.2次选 csv文件 与 Unix/Linux 下常用的 xx xxx形式的配置文件 分别是标准库csv 与 shlex 2.最常用的二进制级配置文件 2.1持久化数据首选shelve 标准库 shelve 2.2shelve的对于一些项目我们需要…

Python的看门狗实现自动化实时对服务器、Windows或Linux文件夹的实时监控

众所周知&#xff0c;在运维过程中&#xff0c;实时获取目标文件夹至关重要&#xff0c;Python的watchdog是用程序来监视文件系统事件Python库&#xff0c;所以用该库可以实现对文件夹的实时监控&#xff0c;filenotify.py代码如下&#xff1a; # -*- coding: utf-8 -*- #!/usr…

在html中样式表的三种类型,css样式有哪几种类型?

CSS样式可以写在哪些地方呢&#xff1f;从CSS 样式代码插入的形式来看基本可以分为以下3种&#xff1a;内联式、嵌入式和外部式三种。下面本篇文章就来给大家介绍一下CSS样式的类型&#xff0c;希望对大家有所帮助。内联式样式内联式css样式表就是把css代码直接写在现有的HTML标…

DIV CSS浏览器的兼容性

1. 文字本身的大小不兼容。同样是font-size:14px的宋体文字&#xff0c;在不同浏览器下占的空间是不一样的&#xff0c;ie下实际占高16px&#xff0c;下留白3px&#xff0c;ff 下实际占高17px&#xff0c;上留白1px&#xff0c;下留白3px&#xff0c;opera下就更不一样了。解决…

记录合规性–关于TCK,规格和测试

使用软件规格非常困难。 不论在哪个地方提出&#xff1b; 您最终遇到了一个大问题&#xff1a;是否已实施并测试了所有指定的内容&#xff1f; 在瀑布驱动的方法学时代&#xff0c;这一直是一个问题&#xff0c;即使在撰写本文的今天&#xff0c;敏捷性和用户故事仍然不能保证您…

arcgis已试图对空几何执行该操作_ArcGIS中地理配准与空间校正的不同

ArcGIS中地理配准与空间校正都是用于数据坐标变换的目的&#xff0c;他们之间有什么区别呢&#xff1f;1、处理对象不同&#xff1a;地理配准针对栅格数据&#xff0c;而空间校正针对矢量数据。因此空间校正需要建立在矢量数据编辑的基础上&#xff0c;空间校正之前应开始编辑。…

【计算机视觉】深度学习视觉领域常用数据集汇总

本文结合笔者在研究生学习、科研期间使用过以及阅读文献了解到的深度学习视觉领域常用的开源数据集&#xff0c;进行介绍和汇总。MNIST深度学习领域的“Hello World!”&#xff0c;入门必备&#xff01;MNIST是一个手写数字数据库&#xff0c;它有60000个训练样本集和10000个测…

CSS Hack 汇总速查一览

由于浏览器之间存在兼容性问题&#xff0c;在制作网页的时候&#xff0c;为了使页面能在不同浏览器中显示相对一致或者其他原因&#xff0c;网页制作人员总结了种种 Hack 方法&#xff1b;在解 决兼容性问题之前&#xff0c;这些方法还经常会用到。接下来&#xff0c;大前端将给…

玩游戏4g计算机的内存不足,玩游戏时出现存储空间不足, 无法完成此操作, 到底是何原因?...

游戏是Just Cause 2(即《正当防卫2》, 对不&#xff1f;这款游戏需要很高的硬件配置&#xff0c;显卡必须在DirectX10以上才行;你的是什么显卡&#xff1f;请确保安装了最新的.NET和DX10&#xff1b;另我搜索到下面的解决方法&#xff0c;仅供参考&#xff1a;解决方法&#xf…

win10安装opcenum_Win10提示Windows无法安装到GPT分区形式磁盘

Win10系统提示Windows无法安装到GPT分区形式磁盘该怎么办&#xff1f;最近有用户反映说在安装Win10原版系统的时候&#xff0c;采用的是U盘安装Win10的方法&#xff0c;到磁盘选择这一项的时候&#xff0c;选中C盘不能安装&#xff0c;提示&#xff1a;Windows无法安装到这个磁…

JPA 2 | EntityManagers,事务及其周围的一切

介绍 对我来说&#xff0c;最令人困惑和不清楚的事情之一是&#xff0c;作为Java开发人员&#xff0c;一直是围绕事务管理的谜团&#xff0c;尤其是JPA如何处理事务管理。 事务什么时候开始&#xff0c;什么时候结束&#xff0c;实体的持久化方式&#xff0c;持久性上下文等等。…

JavaScript高级特征之面向对象笔记

Javascript面向对象&#xff1a;函数&#xff1a; * Arguments对象&#xff1a; * Arguments对象是数组对象 * Arguments对象的length属性可以获取参数的个数 * 利用Arguments对象模拟函数的重载效果&#xff08;javascript中不存在函…

Object.prototype.hasOwnProperty与Object.getOwnPropertyNames

Object.prototype.hasOwnProperty() 所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性&#xff1b;和 in 运算符不同&#xff0c;该方法会忽略掉那些从原型链上继承到的属性。 使用 hasOwnProperty 方法判断属自身…

IE9真的支持CSS3和HTML5?

微软昨天在其2009年专业开发者大会上展示 了下一个版本的Internet Explorer浏览器IE9。 尽管只是一个早期版本&#xff0c;IE开发团队还是比较高调的宣布了IE9的一些改 进&#xff0c;比如速度比之前的IE版本都更快些&#xff0c;缩小与FF和webkit之间的差距(也就是还是赶不上了…