试验1:做的条目不发现
首先,我们必须确保我们的应用是工作性质所做条目不发现。我们可以写的测试以确保通过以下步骤:

1、配置的模拟对象时抛出一个todonotfoundexception findbyid()方法被调用和请求的待办事项条目ID 1L。
2、执行一个GET请求的URL /做/ 1′。
3、确认HTTP状态码返回404。
4、确保返回的视图名称是“错误/ 404′。
5、确保请求转发到URL“/WEB-INF/JSP /错误/ 404 JSP”。
6、验证的todoservice接口findbyid()方法被称为只有一次正确的方法参数(1)。
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;

@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:做条目被发现
第二,我们必须写一个测试以确保控制器正常工作时,全部的入口被发现。我们可以通过下面的步骤:
1、创建待办事项的对象是回来时,我们的服务调用的方法。再次,我们创建返回的Todo对象通过使用我们的测试数据生成器。
2、配置我们的模拟对象创建对象时返回做的findbyid()方法是采用参数1。
3、执行一个GET请求的URL /做/ 1′。
4、确认HTTP状态码返回200。
5、确保返回的视图名称是“做/视图”。
6、确保请求转发到URL“/WEB-INF/JSP /做/视图JSP”。
7、验证的模型被称为“对象ID 1L。
8、验证的模型对象,被称为“的描述是“lorem ipsum”。
9、验证的模型对象,被称为“标题是“foo”。
10、确保我们的模拟对象的findbyid()方法被称为只有一次正确的方法参数(1)。
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;

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

在添加待办事项表格表单提交

再次,我们将首先在我们会为它编写单元测试将在我们的控制方法的预期行为一看。

预期的行为

该控制器的方法处理的添加待办事项报名表是通过以下这些步骤的形式提交:

1、它处理POST请求发送到URL /做/添加”。
2、它会检查作为方法的参数不该BindingResult物体有任何错误。如果发现错误,则返回窗体视图的名称。
3、它增加了通过调用接口的todoservice add()方法的一个新的待办事项的进入和通过表单对象作为方法参数。该方法创建一个新的待办事项条目并返回它。
4、它创造了对添加待办事项输入反馈的消息,将消息添加到redirectattributes对象作为方法参数。
5、它增加了额外的任务进入redirectattributes对象ID。
6、它返回一个重定向视图将请求重定向到登录页面查看待办事项名称。

的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类声明了一些验证约束如下:
 1、一项不做标题是空的。
 2、描述的最大长度是500个字符。
 3、标题的最大长度是100个字符。
如果我们想测试,我们应该为这个控制器的方法写,它是明确的,我们必须确保
 1、该控制器的方法是工作性质当验证失败。
 2、该控制器的方法是工作性质当todo条目被添加到数据库。
让我们看看如何写这些测试。

试验1:验证失败
首先,我们要写一个测试,确保我们的控制器的方法是正常工作,当验证失败。我们可以这样写测试按以下步骤:
 1、创建一个标题,101字。
 2、创建一个描述这501个字符。
 3、通过使用我们的测试数据生成器创建一个新的tododto对象。设置标题和对象的描述。
 4、执行POST请求的URL /做/添加”。设置请求的应用程序/窗体-urlencoded内容类型”。确保内容的形式对象发送请求的身体。设置窗体对象为会话。
 5、确认HTTP状态码返回200。
 6、确认返回的视图名称是“做/添加”。
 7、验证请求转发到URL“/WEB-INF/JSP /做/添加JSP”。
 8、验证我们的模型的属性是场错误的标题和描述领域。
 9、确保我们的模型属性ID无效。
 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;

@Autowired
    private TodoService todoServiceMock;

//Add WebApplicationContext field here

//The setUp() method is omitted.

@Test
    public 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类调用静态方法。这些方法如下:
 1、该createstringwithlength(int length)方法创建一个新的具有给定长度的字符串对象并返回创建的对象。
 2、该convertobjecttoformurlencodedbytes(对象)的方法的对象转换成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条目被添加到数据库
第二,我们要写一个测试以确保控制器正常工作时,一个新的todo条目被添加到数据库。我们可以这样写测试按以下步骤:

1、通过使用测试数据生成器类创建一个对象。设置“合法”的值的名称和创建对象的描述域。
 2、创建一个对象时返回做的todoservice接口add()方法称为。
 3、配置我们的模拟对象创建对象时返回做的add()方法被调用和创建的窗体对象作为方法参数。
 4、执行POST请求的URL /做/添加”。设置请求的应用程序/窗体-urlencoded内容类型”。确保内容的形式对象发送请求的身体。设置窗体对象为会话。
 5、确认HTTP状态码返回302。
 6、确保返回的视图名称是“重定向:待办事项/ {id}”。
 7、确保请求重定向到的URL /做/ 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;

@Autowired
    private TodoService todoServiceMock;

//Add WebApplicationContext field here

//The setUp() method is omitted.

@Test
    public 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框架测试正常”控制器的方法写一些单元测试。这篇教程已经教了四件事:
我们学会了创造出了控制器的方法处理请求。
我们学会了对写断言的测试控制器的方法返回。
我们学会了如何写入控制器的方法使一个视图的单元测试。
我们学会了写控制器的方法处理表单提交的单元测试。
本教程的下一部分介绍了如何编写单元测试REST API。
注:这篇文章的示例应用程序可在GitHub。我建议你检查出来,因为它有一些单元测试是不包括在本博客。

http://java.dzone.com/articles/junit-testing-spring-mvc-1

Unit Testing of Spring MVC的更多相关文章

  1. Unit Testing of Spring MVC Controllers: “Normal” Controllers

    Original link: http://www.petrikainulainen.net/programming/spring-framework/unit-testing-of-spring-m ...

  2. Unit Testing of Spring MVC Controllers: Configuration

    Original Link: http://www.petrikainulainen.net/programming/spring-framework/unit-testing-of-spring-m ...

  3. Unit Testing of Spring MVC Controllers1

    我们的pom.xml文件相关的部分看起来如下: <dependency>    <groupId>com.fasterxml.jackson.core</groupId& ...

  4. Spring MVC Test -Controller

    http://www.petrikainulainen.net/programming/spring-framework/unit-testing-of-spring-mvc-controllers- ...

  5. 玩转单元测试之Testing Spring MVC Controllers

    玩转单元测试之 Testing Spring MVC Controllers 转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html The Spri ...

  6. [Mockito] Spring Unit Testing with Mockito

    It is recommened to write unit testing with Mockito in Spring framework, because it is much faster w ...

  7. MVC Unit Testing学习笔记

    MVC Unit Testing 参考文档: 1.http://www.asp.net/mvc/overview/testing 2.http://www.asp.net/mvc/tutorials/ ...

  8. 使用MockMvc测试Spring mvc Controller

    概述   对模块进行集成测试时,希望能够通过输入URL对Controller进行测试,如果通过启动服务器,建立http client进行测试,这样会使得测试变得很麻烦,比如,启动速度慢,测试验证不方便 ...

  9. spring mvc 介绍

    Spring MVC Tutorial tag. * * If you do not want to deal with the intricities of the noscript * secti ...

随机推荐

  1. as3 区别中文 英文 数字

    1)英文a-z是65-90,A-Z是97-112 2)数字是0-9是,48-57 3)上万的都是中文字符 var str:String = "hello world! 你好世界! 88!&q ...

  2. React 简单介绍

    React 简单介绍 作者 RK_CODER 关注 2014.12.10 17:37* 字数 2516 阅读 55715评论 6喜欢 70 why React? React是Facebook开发的一款 ...

  3. mongodb基础学习1-基本说明及安装

    以前看过一些mongodb的视频,但只看到一半没有看完,也没有同步安装软件动手操作,正好最近没事,打算花点时间从头学习一遍,边学习边动手操作,学习的过程在此进行记录. 好了,下面说一下今天的学习内容. ...

  4. Ubuntu技巧之清理系统中无用的软件包

    如果你频繁的在你的系统中安装/卸载,那么不时的清理一下你的系统是十分必要的. 在Ubuntu终端中执行如下命令: sudo apt-get autoremove 屏幕输出是这个样子的: Reading ...

  5. while

    public class TestWhile { /** * 测试循环 */ public static void main(String[] args) { int a=1; while(a< ...

  6. 吴裕雄 python 数据处理(2)

    import pandas as pd data = pd.read_csv("F:\\python3_pachongAndDatareduce\\data\\pandas data\\hz ...

  7. Zabbix安装(server和agent)及基本配置

    简介 zabbix([`zæbiks])是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案. zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通知机制 ...

  8. bug-ajax

    ajax 的get 方法参数及url的长度有限制 问题:url的参数只有一个url?id=101001000000000000000001 参数过长,ajax会报错. 解决方法:1,把get换成pos ...

  9. hdoj1075-What Are You Talking About 【map】

    http://acm.hdu.edu.cn/showproblem.php?pid=1075 What Are You Talking About Time Limit: 10000/5000 MS ...

  10. 52. N-Queens II (Array; Back-Track)

    Follow up for N-Queens problem. Now, instead outputting board configurations, return the total numbe ...