Getting Ready

测试相关Maven dependency如下:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.0.3.RELEASE</version>
<scope>test</scope>
</dependency> <dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>

关于Spring项目的一些依赖如(spring-context, spring-web, spring-webmvc, spring-beans),这里就不列举了

Example

Reference Class

Controller 如下:

@Controller
@RequestMapping("/")
public class DemoController { @Autowired
private TestProjectService testProjectService; @RequestMapping(value = "jsonCompare", method = RequestMethod.POST)
@ResponseBody
public List<FieldComparisonFailure> jsonCompare(@RequestParam("expect") String expect, @RequestParam("actual") String actual, ModelMap model,
HttpSession session) { List<FieldComparisonFailure> list = testProjectService.compare(expect, actual); return list;
} }

FieldComparisonFailure类如下

/**
* Models a failure when comparing two fields.
*/
public class FieldComparisonFailure {
private final String field;
private final Object expected;
private final Object actual; public FieldComparisonFailure(String field, Object expected, Object actual) {
this.field = field;
this.expected = expected;
this.actual = actual;
} public String getField() {
return field;
} public Object getExpected() {
return expected;
} public Object getActual() {
return actual;
}
}

TestProjectService接口如下:

public interface TestProjectService {
public List<FieldComparisonFailure> compare(String expect, String actual); }

TestProjectServiceImpl 具体实现是比较两个Json字符串 返回一个List

@Service
public class TestProjectServiceImpl implements TestProjectService { @Override
public List<FieldComparisonFailure> compare(String expect, String actual) { Comparator comparator = new Comparator();
List<FieldComparisonFailure> list = new ArrayList<FieldComparisonFailure>(); try {
list = comparator.compare(expect, actual);
} catch (JSONException e) {
e.printStackTrace();
}
return list;
} }

##转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html

Unit Test

首先来看一个独立的单元测试方式, 这个例子用Mockito 模拟service层以便隔离controller的测试。

package com.wadeshop.controller;

import static org.mockito.Mockito.when;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.hamcrest.Matchers.hasSize; import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import com.google.common.collect.ImmutableList;
import com.wadeshop.service.TestProjectService;
import com.wadeshop.entity.FieldComparisonFailure; public class DemoControllerTest_mock { @Mock
private TestProjectService testProjectService; @InjectMocks
private DemoController demoController; private MockMvc mockMvc; @Before
public void setup() { // initialize mock object
MockitoAnnotations.initMocks(this); // Setup Spring test in standalone mode
this.mockMvc = MockMvcBuilders.standaloneSetup(demoController).build();
} @Test
public void test() throws Exception { //prepare test data
FieldComparisonFailure e1 = new FieldComparisonFailure("Number", "3", "4");
FieldComparisonFailure e2 = new FieldComparisonFailure("Number", "1", "2"); //actually parameter haven't use, service was mocked
String expect = "";
String actual = ""; //Sets a return value to be returned when the method is called
when(testProjectService.compare(expect, actual)).thenReturn(ImmutableList.of(e1, e2)); //construct http request and expect response
this.mockMvc
.perform(post("/jsonCompare")
.accept(MediaType.APPLICATION_JSON)
.param("actual", actual)
.param("expect", expect))
.andDo(print()) //print request and response to Console
.andExpect(status().isOk())
.andExpect(content().contentType("application/json;charset=UTF-8"))
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[0].field").value("Number"))
.andExpect(jsonPath("$[0].expected").value("3"))
.andExpect(jsonPath("$[0].actual").value("4"))
.andExpect(jsonPath("$[1].field").value("Number"))
.andExpect(jsonPath("$[1].expected").value("1"))
.andExpect(jsonPath("$[1].actual").value("2")); //verify Interactions with any mock
verify(testProjectService, times(1)).compare(expect, actual);
verifyNoMoreInteractions(testProjectService);
}
}

@Mock: 需要被Mock的对象

@InjectMocks: 需要将Mock对象注入的对象, 此处就是Controller

Before test

初始化Mock对象, 通过MockMvcBuilders.standaloneSetup模拟一个Mvc测试环境,注入controller, 通过build得到一个MockMvc, 后面就用MockMvc的一些API做测试。

这不是真实的Spring MVC环境,如果要模拟真实环境需要用 MockMvcBuilders.webAppContextSetup(webApplicationContext).build(), 见下文。

测试方法里面需要构建测试数据,mock service调用方法,返回一个ImmutableList (google-collections 谷歌的集合库)

然后构造http请求并且传入参数执行, 最后断言验证期望结果, 关于JsonPath的使用请参考http://goessner.net/articles/JsonPath/

运行

andDo(print()) 打印到控制台的信息如下

MockHttpServletRequest:
HTTP Method = POST
Request URI = /jsonCompare
Parameters = {actual=[], expect=[]}
Headers = {Accept=[application/json]} Handler:
Type = com.wadeshop.controller.DemoController Async:
Was async started = false
Async result = null Resolved Exception:
Type = null ModelAndView:
View name = null
View = null
Model = null FlashMap: MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[application/json;charset=UTF-8]}
Content type = application/json;charset=UTF-8
Body = [{"field":"Number","actual":"4","expected":"3"},{"field":"Number","actual":"2","expected":"1"}]
Forwarded URL = null
Redirected URL = null
Cookies = []

##转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html

Integration Testing

再来看集成Web环境方式, 这次仍然使用Spring MVC Test 但还需要加载 WebApplicationContext

import static org.hamcrest.Matchers.hasSize;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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; @RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration(value = "src/main/webapp")
@ContextConfiguration("file:src/main/resources/applicationContext.xml") public class DemoControllerTest { @Autowired
private WebApplicationContext wac;
private MockMvc mockMvc; @Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
} @Test
public void test() throws Exception {
String actual = "{\"orderNumber\": \"4955\",\"orderVersion\": \"1\"}";
String expect = "{\"orderNumber\": \"4956\",\"orderVersion\": \"1\"}"; this.mockMvc
.perform(post("/jsonCompare")
.accept(MediaType.APPLICATION_JSON)
.param("actual", actual)
.param("expect", expect))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().contentType("application/json"))
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].field").value("orderNumber"))
.andExpect(jsonPath("$[0].actual").value("4955"))
.andExpect(jsonPath("$[0].expected").value("4956"));
}
}

@RunWith: 告诉Junit使用 Spring-Test 框架, 允许加载web 应用程序上下文。

@WebAppConfiguration: 表明该类会使用web应用程序的默认根目录来载入ApplicationContext, value = "src/main/webapp" 可以不填,默认此目录

@ContextConfiguration: 指定需要加载的spring配置文件的地址 ("file:src/main/resources/applicationContext.xml")

@Autowired WebApplicationContext wac:注入web环境的ApplicationContext容器;

使用MockMvcBuilders.webAppContextSetup(wac).build()来创建一个MockMvc进行测试, 此为模拟真实的Spring MVC环境

测试过程和前面一个例子大体相似,唯一的区别就是,这次传入的是真实的参数,调用真实的service取得返回值。

运行时间比较长

控制台信息

MockHttpServletRequest:
HTTP Method = POST
Request URI = /jsonCompare
Parameters = {actual=[{"orderNumber": "4955","orderVersion": "1"}], expect=[{"orderNumber": "4956","orderVersion": "1"}]}
Headers = {Accept=[application/json]} Handler:
Type = com.wadeshop.controller.DemoController Async:
Was async started = false
Async result = null Resolved Exception:
Type = null ModelAndView:
View name = null
View = null
Model = null FlashMap: MockHttpServletResponse:
Status = 200
Error message = null
Headers = {Content-Type=[application/json]}
Content type = application/json
Body = [{"field":"orderNumber","actual":"4955","expected":"4956"}]
Forwarded URL = null
Redirected URL = null
Cookies = []

从上面的例子来看集成测试Spring MVC controller是不是也很简单, 稍微配置一下就行了。

##转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html

总结

单元测试过程无非就这三部曲:

  1. 准备 (测试环境,测试数据)
  2. 执行 (构造请求传入参数执行)
  3. 断言 (验证结果)

Troubleshooting

如果发现一些NoClassDefFoundError, 估计依赖的jar版本太旧了。

import 哪些类不要弄错了,有些需要static import, IDE工具不一定会提示导入

SpringMVC Controller层的单元测试的更多相关文章

  1. Spring Boot从Controller层进行单元测试

    单元测试是程序员对代码的自测,一般公司都会严格要求单元测试,这是对自己代码的负责,也是对代码的敬畏. 一般单元测试都是测试Service层,下面我将演示从Controller层进行单元测试. 无参Co ...

  2. 关于Spring MVC Controller 层的单元测试

    关于Spring MVC Controller 层的单元测试 测试准备工作: 1.搭建测试Web环境 2.注入Controller 类 3.编写测试数据 测试数据的文件名一定要与测试类的文件名相同,比 ...

  3. ssm controller层 junit单元测试

    原文链接:https://www.cnblogs.com/oucbl/p/5943743.html springmvc controller junit 测试 作者:blouc@qq.com本文为作者 ...

  4. springmvc controller层接收List类型的参数

    Spring MVC在接收集合请求参数时,需要在Controller方法的集合参数里前添加@RequestBody,而@RequestBody默认接收的enctype (MIME编码)是applica ...

  5. springmvc,controller层在接收浏览器url传来的参数带中文乱码问题。

    请求地址:http://localhost:8080/saveFlashSale?fsRemark=哈哈哈哈哈 接收方法:@RequestMapping("/saveFlashSale&qu ...

  6. PowerMock+SpringMVC整合并测试Controller层方法

    PowerMock扩展自Mockito,实现了Mockito不支持的模拟形式的单元测试.PowerMock实现了对静态方法.构造函数.私有方法以及final方法的模拟支持,对静态初始化过程的移除等强大 ...

  7. SpringBoot—单元测试模板(controller层和service层)

    介绍 概述   在开发过程中,我们经常会一股脑的写各种业务逻辑,经常等全部大功告成的时候,打个jar包放环境里跑跑看看能不能通,殊不知在各个业务方法中已经漏洞百出,修复一个打一个包,再继续修复,这种效 ...

  8. SpringMVC在Controller层中注入request的坑

    记一次为了节省代码没有在方法体中声明HttpServletRequest,而用autowire直接注入所钻的坑 结论 给心急的人. 直接在Controller的成员变量上使用@Autowire声明Ht ...

  9. SpringMVC的controller层接收来自jsp页面通过<a href="/user/userUpdateInfo/>的中文乱码问题

    这种情况是,jsp页面的中文正常显示,数据的中文也是正常显示,但是在Controller层接收到的中文是乱码,如下图所示: 解决方法:在Controller层对前台传递的中文乱码进行处理,将它转换成u ...

随机推荐

  1. Android-bindService远程服务(Aidl)-初步

    之前上一篇讲解到本地服务,本地服务只能在自身APP中Activity访问Service,调用Service里面到方法等操作 如果想A应用访问B应用里面的方法,属于跨进程调用,如果Android不特供这 ...

  2. Sublime Text 3 Settings

    Preferences - Setting User - > : { "font_size": 14, "word_wrap": true, " ...

  3. c#设计模式之:外观模式(Facade)

    一.引言 在软件开发过程中,客户端程序经常会与复杂系统的内部子系统进行耦合,从而导致客户端程序随着子系统的变化而变化,然而为了将复杂系统的内部子系统与客户端之间的依赖解耦,从而就有了外观模式,也称作 ...

  4. C# 等待框

    今天发现dev控件自带了等待框,调用方便,而且不会阻塞主线程. 拉一个窗体,界面上放两个按钮,显示等待框,隐藏. <Window x:Class="WaitDialogTest.Mai ...

  5. [Swift]堆栈Stack的两种版本:(1)用类包装Stack (2)用泛型包装Stack

    堆栈是一个在计算机科学中经常使用的抽象数据类型.堆栈中的物体具有一个特性: 最后一个放入堆栈中的物体总是被最先拿出来, 这个特性通常称为后进先出(LIFO)队列. 堆栈中定义了一些操作. 两个最重要的 ...

  6. [Swift]扩展String类:extension String

    请参考本博客另一篇技术博文: <[Swift]字符串(String类.NSString类)常用操作> extension String { //获取字符串首字符 var first: St ...

  7. jquery事件二 -- 选项卡,失去焦点

    以之前的选项卡例子为原版,当选上某一个选项卡的时候,选项卡周围会有一个蓝色的边框影响视觉体验,那么应该怎么去掉这个边框色呢?只需要加一行blur()--失去焦点函数就可以了 <!DOCTYPE ...

  8. Flink学习笔记:Operators串烧

    本文为<Flink大数据项目实战>学习笔记,想通过视频系统学习Flink这个最火爆的大数据计算框架的同学,推荐学习课程: Flink大数据项目实战:http://t.cn/EJtKhaz ...

  9. Python3安装使用SaltStack以及salt-api详解

    序言 最近在使用salt-api做主机批量管理部署,整理一下文档.之前使用saltstack 多用于命令行管理,自己做web版的自动化管理平台时,发现命令行的些许局限性,接触到salt-api,找到了 ...

  10. CF1097D Makoto and a Blackboard(期望)

    link 题目大意:给您一个数 n, 每次从n的所有约数(包含1.n)中等概率选出一个约数替换n,重复操作k次,求最后结果期望值%1e9+7. 题解:考虑暴力,我们设f(n,k)代表答案,则有f(n, ...