对Controller进行单元测试是Spring框架原生就支持的能力,它可以模拟HTTP客户端发起对服务地址的请求,可以不用借助于诸如Postman这样的外部工具就能完成对接口的测试。

具体来讲,是由Spring框架中的spring-test模块提供的实现,详见MockMvc。

如下将详细阐述如何使用MockMvc测试框架实现对“Spring Controller”进行单元测试,基于Spring Boot开发框架进行验证。

添加测试框架依赖:

<!-- Spring框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
!<-- Spring测试框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 文件操作工具 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>

导入静态工具方法

为了便于在编写测试用例时直接调用测试框架自带的静态方法,首先需要导入这些静态工具方法。

需要导入的静态方法如下:

import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.setup.SharedHttpSessionConfigurer.*;

初始化MockMvc

初始化MockMvc有2种方式:

方式1:明确指定需要测试的“Controller”类进行配置

方式2:基于Spring容器进行配置,包含了Spring MVC环境和所有“Controller”类,通常使用这种方式。

@SpringBootTest
public class TestControllerTest { MockMvc mockMvc; // 初始化MockMvc
@BeforeEach
void setUp(WebApplicationContext wac) {
// 方式1:明确指定需要测试的“Controller”类
this.mockMvc = MockMvcBuilders.standaloneSetup(new TestController()).build(); // 方式2:基于Spring容器进行配置,包含了Spring MVC环境和所有“Controller”类。
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
}

另外,还可以对MockMvc进行全局配置。

// 全局配置MockMvc
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
.defaultRequest(get("/").accept(MediaType.APPLICATION_JSON)) // 默认请求路径
.apply(sharedHttpSession()) // 配置session
.alwaysExpect(status().isOk()) // 预期响应状态码
.alwaysExpect(content().contentType("application/json;charset=UTF-8")) // 预期内容类型
.build();

执行测试

MockMvc支持对常见的HTTP方法,如:GET,POST,PUT,DELETE等,甚至还支持文件上传请求。

测试GET接口

// 访问GET接口:不带参数
@Test
public void testSimpleGet() throws Exception {
MvcResult result = this.mockMvc.perform(get("/test/simple/get")
.accept(MediaType.APPLICATION_JSON)) // 接受JSON格式响应消息
.andReturn(); // 获取返回结果
Assertions.assertEquals("OK", result.getResponse().getContentAsString());
} // 访问GET接口:带URL参数
@Test
public void testParamGet() throws Exception {
int id = 10;
// 方式1:在URI模板中指定参数
//MvcResult result = this.mockMvc.perform(get("/test/param/get?id={id}", id).accept(MediaType.APPLICATION_JSON)).andReturn(); // 方式2:通过param()方法指定参数
//MvcResult result = this.mockMvc.perform(get("/test/param/get").param("id", String.valueOf(id)).accept(MediaType.APPLICATION_JSON)).andReturn(); // 方式3:通过queryParam()方法指定参数
MvcResult result = this.mockMvc.perform(get("/test/param/get").queryParam("id", String.valueOf(id)).accept(MediaType.APPLICATION_JSON)).andReturn();
Assertions.assertEquals("OK: " + id, result.getResponse().getContentAsString());
}

测试POST接口

// 传递表单参数
@Test
public void testSimplePost() throws Exception {
int id = 10; // 调用param()方法传递参数
MvcResult result = this.mockMvc.perform(post("/test/simple/post")
.param("id", String.valueOf(id))
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.accept(MediaType.APPLICATION_JSON))
.andReturn();
Assertions.assertEquals("{\"id\":10}", result.getResponse().getContentAsString());
} // 传递JSON参数
@Test
public void testSimplePostJson() throws Exception {
// 调用content()方法传递json字符串参数
Subject subject = new Subject();
subject.setId(10);
String content = JSON.toJSONString(subject);
MvcResult result = this.mockMvc.perform(post("/test/simple/post/json")
.content(content)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andReturn();
Assertions.assertEquals("{\"id\":10}", result.getResponse().getContentAsString());
}

测试文件上传

@Test
public void testFileUploadSingle() throws Exception {
File file = new File("C:\\Users\\xxx\\Downloads\\test.jpg");
String fileName = FilenameUtils.getName(file.getName());
byte[] bytes = FileUtils.readFileToByteArray(file);
MockMultipartFile mockMultipartFile = new MockMultipartFile("file", fileName, MediaType.MULTIPART_FORM_DATA_VALUE, bytes);
this.mockMvc.perform(multipart("/test/upload/single").file(mockMultipartFile))
.andExpect(status().isOk())
.andExpect(content().string("OK"))
.andDo(print());
}

定义预期结果

断言响应结果时,有2种方式:

1.使用JUnit提供的Assert断言工具判断返回结果,这是一种非常普遍和常见的方式

2.在MockMvc框架中可以通过andExpect()方法定义一个或多个预期结果,当其中一个期望结果断言失败时,就不会断言其他期望值了

// 使用Junit断言工具判断返回结果是否符合预期
@Test
public void testAssertResult() throws Exception {
MvcResult result = this.mockMvc.perform(get("/test/simple/get").accept(MediaType.APPLICATION_JSON)).andDo(print()).andReturn();
Assert.assertEquals("OK", result.getResponse().getContentAsString());
} // 在MockMvc框架中定义预期结果
@Test
public void testExpectations() throws Exception {
this.mockMvc.perform(get("/test/simple/get").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()) // 预期响应状态码为200
.andExpect(content().string("OK")) // 预期返回值为字符串“OK”
.andDo(print());
}

相比于使用Junit的断言工具判断返回结果,在MockMvc框架中直接定义预期结果进行断言检查更加简洁。

写在最后

使用Spring提供的测试框架MockMvc可以非常方便地实现对HTTP服务接口进行单元测试,不要把基础的功能验证工作都交给测试童鞋,应该通过单元测试来保证代码迭代的稳定性。

【参考】

https://blog.csdn.net/coolcoffee168/article/details/88638042 springboot 单元测试 (controller层) 方法 -- MockMvc

如何对Spring MVC中的Controller进行单元测试的更多相关文章

  1. Spring MVC中的Controller是Serlvet吗?

    1. Controller不是Servlet DispatcherServler是Spring MVC中的唯一Servlet,(这点可通过查看FrameworkServlet的子类确认) Servle ...

  2. spring mvc中的文件上传

    使用commons-fileupload上传文件所需要的架包有:commons-fileupload 和common-io两个架包支持,可以到Apache官网下砸. 在配置文件spring-mvc.x ...

  3. Spring MVC中一般 普通类调用service

    在Spring MVC中,Controller中使用service只需使用注解@Resource就行,但是一般类(即不使用@Controller注解的类)要用到service时,可用如下方法: 1.S ...

  4. Spring MVC中一般类使用service

    在Spring MVC中,Controller中使用service只需使用注解@Resource就行,但是一般类(即不使用@Controller注解的类)要用到service时,可用如下方法: 1.S ...

  5. Spring MVC 中的基于注解的 Controller【转】

    原文地址:http://my.oschina.net/abian/blog/128028 终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 H ...

  6. Spring MVC中基于注解的 Controller

         终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法以响 ...

  7. Spring MVC中,事务是否可以加在Controller层

    一般而言,事务都是加在Service层的,但是爱钻牛角尖的我时常想:事务加在Controller层可不可以.我一直试图证明事务不止可以加在Service层,还可以加在Controller层,但是没有找 ...

  8. Spring MVC 中的基于注解的 Controller(转载)

           终于来到了基于注解的 Spring MVC 了.之前我们所讲到的 handler,需要根据 url 并通过 HandlerMapping 来映射出相应的 handler 并调用相应的方法 ...

  9. Http请求中Content-Type讲解以及在Spring MVC中的应用

    引言: 在Http请求中,我们每天都在使用Content-type来指定不同格式的请求信息,但是却很少有人去全面了解content-type中允许的值有多少,这里将讲解Content-Type的可用值 ...

随机推荐

  1. 怎样修改自己的登录shell

    问题 我只是Linux系统的普通用户,没有权限调用chsh命令修改自己的登录shell. bash-4.1$ chsh --help Usage: chsh [ -s shell ] [ --list ...

  2. spring cloud --- config 配置中心 [本地、git获取配置文件]

    spring boot      1.5.9.RELEASE spring cloud    Dalston.SR1 1.前言 spring cloud config 配置中心是什么? 为了统一管理配 ...

  3. python 迭代器链式处理数据

    pytorch.utils.data可兼容迭代数据训练处理,在dataloader中使用提高训练效率:借助迭代器避免内存溢出不足的现象.借助链式处理使得数据读取利用更高效(可类比操作系统的资源调控) ...

  4. 小程序云开发请求第三方http或https接口

    1.新建http云函数,选中http云函数,右键,打开终端,安装依赖: npm install request-promise 2.http.js引入request-promise用于做网络请求 va ...

  5. 【C】C语言大作业——学生学籍管理系统

    文章目录 学生管理系统 界面 主界面 登陆界面 注册界面 管理界面 学生界面 退出界面 链接 注意 学生管理系统 学C语言时写的一个大作业,弄了一个带图形界面的,使用的是VS配合EasyX图形库进行实 ...

  6. Configmap-K8s容器的配置文件

    传递配置给容器化应用程序有几种方式: 嵌入应用本身: 通过命令行传递参数: 通过环境变量传递参数: 在k8s中无论你有没有使用configmap,以下方法均可以配置应用程序: 向容器传递命令行参数:c ...

  7. MVC框架---转

    浅析MVC模式 摘要:MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面 ...

  8. 记一次简单的Oracle离线数据迁移至TiDB过程

    背景 最近在支持一个从Oracle转TiDB的项目,为方便应用端兼容性测试需要把Oracle测试环境的库表结构和数据同步到TiDB中,由于数据量并不大,所以怎么方便怎么来,这里使用CSV导出导入的方式 ...

  9. 乡亲们,我们创建了 Dapr 中文交流频道

    我们创建了 Dapr 中文交流 QQ 频道,欢迎大家加入!加入方式在文章最后一节. 为什么要创建频道? 解决什么问题 专业性,"你可以在我们群里面钓鱼,因为都是水" 你肯定加过非常 ...

  10. vue 快速入门 系列 —— 实例方法(或 property)和静态方法

    其他章节请看: vue 快速入门 系列 实例方法(或 property)和静态方法 在 Vue(自身) 项目结构 一文中,我们研究了 vue 项目自身构建过程,也知晓了 import Vue from ...