这可能是解决你Spring MVC接口漏洞百出的关键

1. 前言

在 Java 开发中接触的开发者大多数不太注重对接口的测试,结果在联调对接中出现各种问题。也有的使用 Postman 等工具进行测试,虽然在使用上没有什么问题,如果接口增加了权限测试起来就比较恶心了。所以建议在单元测试中测试接口,保证在交付前先自测接口的健壮性。今天就来分享一下胖哥在开发中是如何对 Spring MVC 接口进行测试的。

在开始前请务必确认添加了Spring Boot Test相关的组件,在最新的版本中应该包含以下依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions>
</dependency>

本文是在Spring Boot 2.3.4.RELEASE下进行的。

2. 单独测试控制层

如果我们只需要对控制层接口(Controller)进行测试,且该接口不依赖@Service@Component等注解声明的 Spring Bean 时,可以借助@WebMvcTest来启用只针对 Web 控制层的测试,例如

@WebMvcTest
class CustomSpringInjectApplicationTests {@AutowiredMockMvc mockMvc;@SneakyThrows@Testvoid contextLoads() {mockMvc.perform(MockMvcRequestBuilders.get("/foo/map")).andExpect(ResultMatcher.matchAll(status().isOk(),content().contentType(MediaType.APPLICATION_JSON),jsonPath("$.test", Is.is("hello")))).andDo(MockMvcResultHandlers.print());}}

这种方式要快的多,它只加载了应用程序的一小部分。但是如果你涉及到服务层这种方式是不凑效的,我们就需要整体测试了方了。

3. 整体测试

大多数 Spring Boot 下的接口测试是整体而又全面的测试,涉及到控制层、服务层、持久层等方方面面,所以需要加载比较完整的 Spring Boot 上下文。这时我们可以这样做,声明一个抽象的测试基类:

package cn.felord.custom;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;/*** 测试基类,* @author felord.cn*/
@SpringBootTest
@AutoConfigureMockMvc
abstract class CustomSpringInjectApplicationTests {/*** The Mock mvc.*/@AutowiredMockMvc mockMvc;// 其它公共依赖和处理方法
}

只有当@AutoConfigureMockMvc存在时MockMvc才会被注入 Spring IoC。

然后针对具体的控制层进行如下测试代码的编写:

package cn.felord.custom;import lombok.SneakyThrows;
import org.hamcrest.core.Is;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;/*** 测试FooController.** @author felord.cn*/
public class FooTests extends CustomSpringInjectApplicationTests {/*** /foo/map接口测试.*/@SneakyThrows@Testvoid contextLoads() {mockMvc.perform(MockMvcRequestBuilders.get("/foo/map")).andExpect(ResultMatcher.matchAll(status().isOk(),content().contentType(MediaType.APPLICATION_JSON),jsonPath("$.test", Is.is("bar")))).andDo(MockMvcResultHandlers.print());}
}

4. MockMvc 测试

集成测试时,希望能够通过输入 URL 对 Controller 进行测试,如果通过启动服务器,建立 http client 进行测试,这样会使得测试变得很麻烦,比如,启动速度慢,测试验证不方便,依赖网络环境等,为了可以对 Controller 进行测试,所以引入了MockMvc

MockMvc实现了对 Http 请求的模拟,能够直接使用网络的形式,转换到 Controller 的调用,这样可以使得测试速度快、不依赖网络环境,而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便。接下来我们来一步步构造一个测试的模拟请求,假设我们存在一个下面这样的接口:

@RestController
@RequestMapping("/foo")
public class FooController {@Autowiredprivate MyBean myBean;@GetMapping("/user")public Map<String, String> bar(@RequestHeader("Api-Version") String apiVersion, User user) {Map<String, String> map = new HashMap<>();map.put("test", myBean.bar());map.put("version", apiVersion);map.put("username", user.getName());//todo your businessreturn map;}
}

参数设定为name=felord.cn&age=18,那么对应的 HTTP 报文是这样的:

GET /foo/user?name=felord.cn&age=18 HTTP/1.1
Host: localhost:8888
Api-Version: v1

可以预见的返回值为:

{"test": "bar","version": "v1","username": "felord.cn"
}

事实上对接口的测试可以分为以下几步。

构建请求

构建请求由MockMvcRequestBuilders负责,他提供了请求方法(Method),请求头(Header),请求体(Body),参数(Parameters),会话(Session)等所有请求的属性构建。/foo/user接口的请求可以转换为:

MockMvcRequestBuilders.get("/foo/user").param("name", "felord.cn").param("age", "18").header("Api-Version", "v1")

执行 Mock 请求

然后由MockMvc执行 Mock 请求:

mockMvc.perform(MockMvcRequestBuilders.get("/foo/user").param("name", "felord.cn").param("age", "18").header("Api-Version", "v1"))

对结果进行处理

请求结果被封装到ResultActions对象中,它封装了多种让我们对 Mock 请求结果进行处理的方法。

对结果进行预期期望

ResultActions#andExpect(ResultMatcher matcher)方法负责对响应的结果的进行预期期望,看看是否符合测试的期望值。参数ResultMatcher负责从响应对象中提取我们需要期望的部位进行预期比对。

假如我们期望接口/foo/user返回的是JSON,并且 HTTP 状态为200,同时响应体包含了version=v1的值,我们应该这么声明:

   ResultMatcher.matchAll(MockMvcResultMatchers.status().isOk(),MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON),MockMvcResultMatchers.jsonPath("$.version", Is.is("v1")));

JsonPath是一个强大的 JSON 解析类库,请通过其项目仓库https://github.com/json-path/JsonPath了解。

对响应进行处理

ResultActions#andDo(ResultHandler handler)方法负责对整个请求/响应进行打印或者 log 输出、流输出,由MockMvcResultHandlers工具类提供这些方法。我们可以通过以上三种途径来查看请求响应的细节。

例如/foo/user接口:

MockHttpServletRequest:HTTP Method = GETRequest URI = /foo/userParameters = {name=[felord.cn], age=[18]}Headers = [Api-Version:"v1"]Body = nullSession Attrs = {}Handler:Type = cn.felord.xbean.config.FooControllerMethod = cn.felord.xbean.config.FooController#urlEncode(String, Params)Async:Async started = falseAsync result = nullResolved Exception:Type = nullModelAndView:View name = nullView = nullModel = nullFlashMap:Attributes = nullMockHttpServletResponse:Status = 200Error message = nullHeaders = [Content-Type:"application/json"]Content type = application/jsonBody = {"test":"bar","version":"v1","username":"felord.cn"}Forwarded URL = nullRedirected URL = nullCookies = []

获取返回结果

如果你希望进一步处理响应的结果,也可以通过ResultActions#andReturn()拿到MvcResult类型的结果进行进一步的处理。

完整的测试过程

通常andExpect是我们必然会选择的,而andDoandReturn在某些场景下会有用,它们两个是可选的。我们把上面的连在一起。

@Autowired
MockMvc mockMvc;@SneakyThrows
@Test
void contextLoads() {mockMvc.perform(MockMvcRequestBuilders.get("/foo/user").param("name", "felord.cn").param("age", "18").header("Api-Version", "v1")).andExpect(ResultMatcher.matchAll(status().isOk(),content().contentType(MediaType.APPLICATION_JSON),jsonPath("$.version", Is.is("v1")))).andDo(MockMvcResultHandlers.print());}

这种流式的接口单元测试从语义上看也是比较好理解的,你可以使用各种断言、正例、反例测试你的接口,最终让你的接口更加健壮。

5. 总结

一旦你熟练了这种方式,你编写的接口将更加具有权威性而不会再漏洞百出,甚至有时候你也可以使用 Mock 来设计接口,使之更加贴合业务。所以 CRUD 不是完全没有技术含量,高质量高效率的 CRUD 往往需要这种工程化的单元测试来支撑。好了今天的分享就到这里,我是:码农小胖哥,多多关注,多多支持。

胖哥花了一年搞了个10w访问的小网站

2020-10-13

Restful接口的版本控制

2020-10-12

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

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

相关文章

由苹果的低级Bug想到的

前段时间的问题&#xff0c;本来想写篇文章说说代码规范&#xff0c;code review相关的&#xff0c;不过看到果壳已经有篇很棒的文章&#xff0c;借来放在这里&#xff0c;也是对自己的指导。果壳很好&#xff0c;里面的文章都很有很有含金量&#xff0c;建议大家都收藏起来 原…

关于人工智能不会使大脑变懒惰的议论文_台湾人工智能学校执行长陈升玮:孩子成为这型人,就不怕被AI取代...

陪伴每个父母和小孩&#xff0c;成为更好的自己。 搜寻公众号 "亲子天下"(微信 ID&#xff1a;cn-parenting)关注 世界经济论坛〈工作大未来〉报告二○一六年的预测&#xff1a;二○二一年全世界会产生两百万个新工作&#xff0c;但随着人工智能(AI)兴起&#xff0c;…

关于人工智能不会使大脑变懒惰的议论文_人工智能的好处和风险:所有您需要知道的...

​ 人工智能不是想象中的未来。现在,在我们的搜索引擎中,人工智能不仅仅是自动完成,也不仅仅是自动驾驶汽车。从机器学习到机器人,人工智能为我们提供了多种技术,使机器能够感知,理解,学习和行动。 是否考虑将绝佳的AI机会纳入您的业务?在您做出决定之前,这里有一些AI…

有可以模拟钢琴弹奏乐曲的手机软件吗?

我们常常喜欢抱着手机做很多有趣的事。其实现在的智能手机真的可以做很多以前意想不到的事。像是手机里有可以模拟钢琴弹奏乐曲的手机软件&#xff0c;钢琴键盘模拟器很有趣。所以很受年轻的小伙伴欢迎。今天我就在这里和大家分享一下&#xff0c;可以模拟钢琴弹奏乐曲的手机软…

普通计算机可以演奏音乐吗,用电脑演奏的音乐还是音乐吗?

现代电子科技迅猛发展&#xff0c;很多作曲家都用电脑作曲&#xff0c;非常方便&#xff0c;就是演奏的时候也能交给电脑负责&#xff0c;合成多个音轨&#xff0c;演奏出复杂的音乐作品。那么&#xff0c;用电脑演奏的音乐还是音乐吗&#xff1f; 当然是音乐。电脑可以负责人脑…

freepiano 手残党也想弹钢琴(在电脑上弹奏电子钢琴自娱自乐,也许还是有点困难,不如试试freepiano+鼠标宏,这样用简谱就不怕残疾了)

我不懂五线谱&#xff0c;勉强能看看简谱&#xff0c;喜欢音乐&#xff0c;但又手残玩不好乐器&#xff0c;像是钢琴电子琴都是双手一起像打键盘一样弹右手位&#xff0c;没法自己搞伴奏&#xff0c;吉他也按不住大横按的和弦&#xff0c;简直没有音乐细菌。 自己想玩吧&#…

毛利率低至个位数,甚至为负?车载语音赛道「现实很骨感」

作为座舱智能化交互的关键角色之一&#xff0c;语音识别及交互一直以来也是不少汽车品牌的主打功能之一。 比如&#xff0c;随意打断语音助手&#xff0c;连续下达指令&#xff0c;融合更多车身控制功能&#xff0c;甚至是ChatGPT大模型的结合&#xff0c;给了消费者更多的想象…

北森控股,云端HCM解决方案“一哥”胜算几成?

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 近日&#xff0c;云端HCM解决方案“一哥”北森控股有限公司&#xff08;以下简称“北森控股”&#xff09;再度递表港交所&#xff0c;摩根士丹利及中金公司为联席保荐人。 资料来源&#xff1a;北森控股招股书 此次IPO北森…

第四范式,第四次递表!能否凭借SageGPT挤进大模型赛道?

4月24日消息&#xff0c;据港交所文件&#xff0c;北京第四范式智能技术股份有限公司向港交所提交上市申请书&#xff0c;中金公司担任独家保荐人。如能顺利上市&#xff0c;第四范式将成为继商汤科技&#xff08;00020.HK&#xff09;、创新奇智&#xff08;02121.HK&#xff…

机械图样解读——回转面

回转面的形成 直线或曲线绕轴线回转形成的曲面称为回转面。 回转面的表示

机械制图 机械识图 三视图画法认图视频教程

机械制图 机械识图 三视图画法认图视频教程 链接&#xff1a;https://pan.baidu.com/s/1fUtYooCRIkoLN5lyFURWCA 提取码&#xff1a;1612

解读机械图样——斜视图

斜视图是零件向不平行于基本投影面的平面投射所得的视图&#xff0c;用于表达零件上倾斜结构的真实形状&#xff0c;如图1-8所示。对于垂直于斜面的孔和槽&#xff0c;用斜视图可以按照实际的尺寸画出它的形状&#xff0c;也易于标注尺寸。 绘制斜视图时&#xff0c;一般在原图…

【CAD】机械类制图实用功能总结

【CAD】机械类制图实用功能总结 一、简介二、正文&#xff08;一&#xff09;快捷键/快捷字母&#xff08;二&#xff09;实用功能1、块2、零件件号标注及材料表关联&#xff08;Vault&#xff09;3、粗糙度标准更改4、增加图纸页数&#xff08;Vault&#xff09;5、尺寸对齐6、…

Highcharts绘制饼图

1、使用Highcharts制作饼图的效果图如下: 2、对应的JavaScript代码---pie-chart.js如下: $(function () {var chart;var totalMoney=50000$(document).ready(function() {chart = new Highcharts.Chart({chart: {renderTo: pie_chart,plotBackgroundColor: white,//背景颜色…

helm和chart

Helm helm是Kubernetes 应用的包管理工具&#xff0c;主要用来管理 Charts&#xff0c;类似Linux系统的yum。Helm Chart 是用来封装 Kubernetes 原生应用程序的一系列 YAML 文件。可以在你部署应用的时候自定义应用程序的一些 Metadata&#xff0c;以便于应用程序的分发。 he…

控制highcharts饼图四周线的长短

var colors [#08c60c,//绿色#ffc002,//黄色#eb3928,//红色#B5B5B5//灰色 ]; // 创建渐变色 Highcharts.getOptions().colors Highcharts.map(colors, function (color) {return {radialGradient: { cx: 0.5, cy: 0.3, r: 0.7 },stops: [[0, color],[1, Highcharts.Color(colo…

CAD轴测图怎么画?快来试试浩辰CAD超级轴测命令!

很多新手设计师小伙伴&#xff0c;不知道CAD轴测图怎么画&#xff1f;其实很简单&#xff0c;浩辰CAD中的超级轴测功能&#xff0c;可以方便地将CAD平面图转化为轴侧图&#xff0c;是绘制管线系统图的好帮手。今天就和小编一起来看看在浩辰CAD软件中通过调用超级轴测命令来绘制…

使用highchart实现3D饼图(重点是3D图、饼图图例不显示问题)

一&#xff1a;使用highchart实现3D饼图 HTML&#xff1a;需要一个容器来绘图 <div class"dataItemRight" id"trafficData"></div>JS&#xff1a;所需js文件 <script src"js/jquery.min.js" type"text/javascript" c…

TeeChart基础使用手册

TeeChart使用手册 pdf版下载地址 TeeChart库下载 文章目录 TeeChart使用手册1、TeeChart.dll、TeeChart8.ocx、TeeChart.WPF.dll优缺点1.1 TeeChart.dll1.2 TeeChart8.ocx1.3 TeeChart.WPF.dll 2、Windows窗体应用(.NET Framework) TeeChart.dll2.1 创建工程2.2 添加TeeChart…

解读机械图样——基本视图

1.投影基本知识 机械图样的绘制必须按照投影法则进行。零件在太阳光、灯光等光源的照射下会产生影子&#xff0c;在此现象的启示下&#xff0c;假设光源发出的光线能透过零件&#xff0c;则零件表面的顶点、棱线就会在选定的平面上投下影子&#xff0c;产生的平面图形即为投影&…