试验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. python 之九九乘法表

    for i in range(1,10): for j in range(1,i+1): print(f"{j}*{i}={i*j}",end='\t') print() 运行结果 ...

  2. UI5-文档-4.10-Descriptor for Applications

    所有特定于应用程序的配置设置现在将进一步放在一个名为manifest的单独描述符文件中.json.这清楚地将应用程序编码从配置设置中分离出来,使我们的应用程序更加灵活.例如,所有SAP Fiori应用 ...

  3. 迷你MVVM框架 avalonjs 1.3.7发布

    又到每个月的15号了,现在avalon已经固定在每个月的15号发布新版本.这次发布又带来许多新特性,让大家写码更加轻松,借助于"操作数据即操作DOM"的核心理念与双向绑定机制,现在 ...

  4. zookeeper 初步学习

    配置文件: tickTime:这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳. dataDir:顾名思义就是 ...

  5. HDFS 好的文章链接

    http://www.cnblogs.com/linuxprobe/p/5594431.html http://www.daniubiji.cn/archives/596 http://blog.cs ...

  6. javaweb登录界面连接数据库

    实验关键截图 数据库界面 建表 2.登录界面 登陆失败 5 注册页面 5 注册成功 数据库截图

  7. hdoj1087 (DP--LIS)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1087 思路:这题很简单了,纯LIS的解法,没有一点变形,由于数据小,使用O(n^2)LIS解法就足够了 ...

  8. 【校招面试 之 C/C++】第12题 C++ 重载、重写和重定义

    1.成员函数重载特征:   a.相同的范围(在同一个类中): b.函数名字相同: c.参数不同(参数个数不同或者参数类型不同,但是返回值不同不能使重载): d.virtual关键字可有可无. 2.重写 ...

  9. Docker常见问题

    问题 当我使用docke search mysql时,显示如下错误: [root@iZ25u61v97hZ opt]# docker search redis Segmentation Fault o ...

  10. nginx反向代理部署与演示(二)

    我们把LB01作为负载均衡器,WEB01和WEB02作为两台web服务器.   WEB01与WEB02虚拟主机配置如下:   我们修改nginx下的conf/nginx.conf文件,在http{}中 ...