缘起

最近我创建并维护了一个开源项目 http-api-invoker ,该项目实现将 HTTP 请求和接口进行绑定,让 HTTP 接口调用跟调用本地方法一样自然优雅。在写单元测试的时候,因为需要发送 HTTP 请求,而做为一个完整独立的项目,我并不希望对它进行单测还需要依赖其他的项目。最开始我用的是 Mockito。

为了让代码更易于测试,我将真正发送请求的任务交给一个接口(Requestor),然后写了一个默认的实现类,用于发送请求。当需要测试的时候,Mock一个Requestor,于是所有请求并没有真正地发出去,只需要断言这个Mock出来的Requstor发送请求的方法有没有被正确调用就可以。

这个是使用 Mockito 的情况下,我能想到的最好的解决方案了。

但是,这里面有一个问题。默认的 Requestor 实现又如何被独立测试呢? 这让我犯难了,所以项目刚开始的时候并没有对默认的 Requestor 进行单元测试,也没有测试真正发送请求的情况下,代码的逻辑是否正确。

偶遇

不久前的某个深夜,我偶然间看到一篇 InfoQ 的上的文章 Stubbing, Mocking and Service Virtualization Differences for Test and Development Teams 让我充分了解了Mock、打桩和模拟服务的区别和应用场景,受益匪浅。在文章里面介绍了 wiremock 这个框架,于是我找到官网 ,看了一下,文档非常地清晰和完善。感觉如获至宝。

应用

我的项目刚好最需要这样的框架来做单元测试。于是我动手写了测试样例。因为官网有非常详细的文档,而且也查看了一些博客上的入门样例,很快就上手了。深深感觉到它的强大,真的非常振奋人心。具体代码可以查看 CityServiceTest 这个测试类。

使用入门

引入 maven 依赖

<dependency><groupId>com.github.tomakehurst</groupId><artifactId>wiremock-standalone</artifactId><version>2.19.0</version><scope>test</scope>
</dependency>

使用入门示例

我这里写了一个简单的测试用例,完整的真实项目用例可以查看 CityServiceTest

public class HelloWireMockTest {private static final int PORT = 18888;/*** 使用给定的端口号生成WireMockRule实例.* 这里设置之后,启动测试时 WireMock 会使用内嵌的 Jetty 启动一个 Web 服务器并监听指定的端口*/@Rulepublic WireMockRule wireMockRule = new WireMockRule(options().port(PORT));@Testpublic void helloTest() {String uri = "/say/hello";String body = "Hello World!";// 给uri打桩,这个语句表示,拦截 uri 为 /say/hello 的请求回复 "Hello World!"wireMockRule.stubFor(get(urlEqualTo(uri)).willReturn(aResponse().withBody(body)));// 这里我们可以用任何方式发起HTTP的GET请求String result = HttpUtil.get("http://localhost:" + PORT + uri);System.out.println(result);// 断言我们发出的请求返回了我们期望的结果assertEquals(result, body);}

这个测试用例跑起来的时候,WireMock会启动一个Web服务器监听 18888 端口,然后我们预设 /say/hello 接口将返回 Hello World! 这行文本做为响应,接下来我们发一个请求过去,断言拿到的是我们期望的结果。

更强大的是,当发的请求跟我们预设的不匹配的时候,它会明明白白地告诉我们,差异在哪里,例如:

而且它也支持 header 和 cookie 的验证,例如

    @Testpublic void getCityWithHeaders() {Map<String, String> headers = new HashMap<>();String key = "auth";String key2 = "auth2";headers.put(key, "123");headers.put(key2, "321");int id = 1;String uri = "/city/getCityRest/" + id;City mockCity = createCity(id);wireMockRule.stubFor(get(urlEqualTo(uri))// 声明我们的请求必须包含两个指定的 header  .withHeader(key, equalTo(headers.get(key))).withHeader(key2, equalTo(headers.get(key2)))// 一旦有符合要求的请求过来,则返回指定的响应.willReturn(aResponse().withBody(JSON.toJSONString(mockCity))));// 使用 http-api-invoker 框架,只需调用接口的方法,框架会发送相应的 http 请求// 这里 cityService.getCityWithHeaders 方法我们绑定的地址是 /getCityRest/{id}City result = cityService.getCityWithHeaders(id, headers);assertEquals(mockCity, result);}

如果我们发请求的时候,header 没有带上,那么控制台就会打印出下面这报告:

结语

单元测试对于写出健壮且高质量的代码非常有必要。没有单测,开发的时候就像夜里一个人走在没有灯的荒郊野岭,你永远不知道前面等待你的是小坑还是深渊,有时候出了问题自己怎么死的都不知道。而单元测试为这场野外旅行添置了一盏明灯,照亮前面的路,让你每走几步都能知道现在处在什么位置。就算掉坑里,也马上让你知道你现在在坑里,赶紧出来,以防止更糟糕的情况。

一个好的单元测试用例也有很大的学问,我相信,花一些时间学习和了解这些技能是一本万利的事情。与君共勉。

WireMock初体验,一个强大的HTTP 请求模拟测试框架相关推荐

  1. Mockito:一个强大的用于Java开发的模拟测试框架

    介绍 本文将介绍模拟测试框架Mockito的一些基础概念, 介绍该框架的优点,讲解应用Mockito的Java示例. 模拟(Mock)的概念 在软件开发的世界之外, "mock"一 ...

  2. 【Web安全】一款功能强大的Web身份认证测试框架

    关于Raider Raider是一款功能强大的Web身份认证测试框架,该框架被设计用来测试Web应用程序的身份认证机制.虽然像ZAProxy和Burpsuite这样的Web代理工具同样可以允许研究人员 ...

  3. 一个强大而简单的 Python Web框架:web.py

    From:https://www.oschina.net/question/5189_4306 Web.py github 地址:https://github.com/webpy/webpy      ...

  4. H5初体验~一个新手H5前端开发的笔记

    在平凡的事物中,探寻人间的美好. 一.H5开发简介 h5是HTML5 是升级版的HTML标准 1.优势 (1)跨平台性!兼容性好,用H5搭建的站点与应用可以兼容PC端与移动端.Windows与Linu ...

  5. 一个强大的工具来模拟数百万​​并发用户负载测试:Gryphon

    Gryphon是由网易自主研发的能够模拟千万级别并发用户的一个软件,目的是能够用较少的资源来模拟出大量并发用户,并且能够更加真实地进行压力测试, 以解决网络消息推送服务方面的压力测试的问题和传统压力测 ...

  6. 我的Go+语言初体验——【三、spx案例测试(附-视频)】

    欢迎大家参与[我的Go+语言初体验]活动: 活动地址:[https://bbs.csdn.net/topics/603464006?utm_source=1594742339] 安装过程博文:[我的G ...

  7. Flutter初体验(二)—— 创建第一个Flutter APP

    Flutter初体验(二)--- 创建第一个Flutter APP 在第一篇文章 Flutter初体验(一)---Mac 安装配置,学习了配置 Flutter 开发环境,并运行了Demo项目,本篇根据 ...

  8. 我的Go+语言初体验——在Docker建立一个可以用Go+语言开发的容器环境(以Ubuntu容器为例)

    前言 "我的Go+语言初体验" | 征文活动进行中...... 作为一名嵌入式软件工程师的我,在工作中使用高级语言开发的场景不多,但技术的迭代大部分偏向于应用层开发,身为程序员的一 ...

  9. Vue初体验(七)使用Vue实现一个简单的聊天框

    1.实践是检验真理的唯一标准,现在我们做一个简易的聊天窗口,有一个input框,用于用户输入,一个按钮button,用于把用户的输入提交上去,然后又一个列表ul,用于展示我们每次提交的消息. 2.通过 ...

最新文章

  1. 硬不硬你说了算!近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题
  2. gvim 编辑器初学
  3. 对dropout的理解详细版
  4. Liunx 安装mysql 5.6.16
  5. P3501-[POI2010]ANT-Antisymmetry【hash,二分答案】
  6. 录播图的分页使用进度条形式显示
  7. 用java画网状图_如何在背景中绘制一个带网格线的漂亮条形图?
  8. java 通过eclipse编辑器用mysql尝试 连接数据库
  9. [转贴]壮观啊!实拍中国最美公路
  10. php数组中去掉空格,php数组如何去除空格
  11. linux远程桌面密钥,使用 SSH 密钥连接到 Linux VM - Azure Virtual Machines | Microsoft Docs...
  12. c语言:输入三角形的边长求面积
  13. 如何清除redis缓存
  14. ubuntu服务器设置定时自动开关机
  15. Linux下用rar压缩和解压文件
  16. 入行大数据,需要学习哪些编程语言?
  17. Win10ahci模式怎么开启?
  18. 操作系统——Liunx系统基础知识
  19. 应届生就业高峰期,Java程序员面试常犯的5点错误总结
  20. 【朴素贝叶斯】深入浅出讲解朴素贝叶斯算法(公式、原理)

热门文章

  1. 数据结构之二叉树汇总额
  2. jq 移动端网页分享功能_原生javascript实现分享到朋友圈功能 支持ios和android
  3. 图像滤镜艺术---霓虹、浮雕、木刻滤镜
  4. Aspera——数据下载必备神器
  5. android手机刷系统升级,闪回收-怎么刷机安卓系统升级?
  6. 情感伤感语录标题文案
  7. 洛谷P1506-拯救oibh总部(DFS)
  8. with(field)方法
  9. cookie、localStorage和sessionStorage三者的区别
  10. 【java】Java教程