原文出处:http://www.kuqin.com/shuoit/20151023/348629.html

不管是Web系统、还是移动APP,各自在与内部、外部系统之间进行数据交互时,大多数情况下都是依赖接口。在基于接口约定开发的模式下,依赖接口的产出时间如果延迟,将直接影响了整个研发调试的效率;如果不能对接口进行及早测试,那发现问题的时间就要被推迟了。既然双方约定了接口格式,为何不按照这个规范直接测试,何必在乎依赖接口什么时候产出,优先做到及早自测,后续只要替换接口联调通过即可。本文主要讲解基于HTTP协议的API接口模拟,从手工Mock到平台的演变过程。

遇到的困惑

曾经遇到的困扰:在研发过程中接口调试对接难的问题:

场景一:

【需求阶段】新功能开发,Portal依赖计费的接口,双方约定基于接口开发(内部、外部依赖接口场景均通用)

【开发阶段】Portal在开发进行中,计费尚未开发完毕,Portal迟迟不能与计费对接调试(也有可能版本迭代步伐不一致的情况),测试阶段一直被推迟;

另外,即使计费接口开发完毕,Portal需要修改计费约定的接口数据进行调试,当发现没有对方接口权限或者计费没有过多人力资源来配合时,也无法进入更丰富的数据细节调试;

【测试阶段】测试人员无法及早介入到调试阶段进行接口测试,造成发现缺陷的最佳时期被推迟;

场景二:

【需求阶段】Portal前、后端约定基于接口开发

【开发阶段】前端开发完毕,后端接口尚未开发完毕,前端只能硬编码数据进行测试,造成接口对接调试延后,而且每次进行更多场景的数据调试,需要频繁重启服务、本地部署;

研发自测阶段无法及早开展,依赖接口约束大。

场景三:

【需求阶段】移动APP项目依赖后端获取带宽数据的接口

【开发阶段】移动APP端通过后端系统API获取带宽数据,绘制带宽图,APP端绘图工具开发完毕,后端API带宽接口尚未开发完毕,移动APP端只能硬编码数据进行测试,造成对接延后,每次进行更丰富的数据调试,需要频繁重启服务、本地部署;

研发自测阶段无法及早开展,依赖接口约束大。

总而言之,如图所示:

依赖接口开发完毕,才能够进入到接口联调测试阶段,即使Portal的功能开发已经完成,也无法进行自测联调,消耗的等待时间代价是不可估量的,效率低,。

图-1-传统的接口对接调试流程

野蛮的石器时代:手工作坊-Nginx反向代理

要解决在研发过程中接口对接调试难的问题,无非是所需即所有,减少等待时间,增加研发自测环节,同时也让测试及早参与进来,因此需要能够把依赖接口模拟出来(白盒方面的Mock有许多解决方案,这里主要讲的是基于HTTP请求的API Server Mock),以便提高生产效率,改进流程如图所示:

图-2-改进的接口对接调试流程

当前最简单的想法是要解决:基于HTTP请求、固定url、能够正则匹配,在这个需求的驱动下,通过Nginx的反向代理能够解决问题。

匹配具体路径下某html文件

location ~ ^/live/(.*).html$ {root /home/htmlfile/ms;
}
location ~ ^/live/([A-Z0-9]+)$ {
}

定义具体返回码

location ~ ^/schedule/.*.(json)$ {                error_page  404     /404.html;}

请求http://info.schedul.com/schedule/1234.json返回404。

定义其它状态码也是同样道理:

error_page 403 /error/403.html;
error_page 500 501 502 503 504 /error/500.html;

俗话说:术业有专攻,Nginx并不擅长做Mock API的工具,在管理配置文件即使可以通过svn进行管理,依然是维护比较困难,对于不熟悉Nginx的测试工程师,也有一定的学习成本。

拿来主义:不重复造轮子-开源WireMock

经历了Nginx的配置繁琐,决定另寻新路,有开源的WireMock(http://wiremock.org/):

Ø WireMock 是一个灵活的库,用于 Web 服务测试,和其他测试工具不同的是:WireMock 创建一个实际的 HTTP服务器来运行你的 Web 服务以方便测试;

Ø 支持 HTTP 响应存根、请求验证、代理/拦截、记录和回放;

创建一个基于WireMock的JavaProject(运行在tomcat下管理):

图-3-ServerMock Project

web.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9"version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <listener> <display-name>wiremock-startup-listener</display-name> <listener-class>com.github.tomakehurst.wiremock.servlet.WireMockWebContextListener</listener-class> <description>Loads WireMock and populates the servletcontext with its services</description> </listener> <context-param> <param-name>WireMockFileSourceRoot</param-name> <param-value>/WEB-INF/wiremock</param-value> </context-param> <context-param> <param-name>verboseLoggingEnabled</param-name> <param-value>false</param-value> </context-param> <servlet> <servlet-name>wiremock-mock-service-handler-servlet</servlet-name> <servlet-class>com.github.tomakehurst.wiremock.jetty6.Jetty6HandlerDispatchingServlet</servlet-class> <init-param> <param-name>RequestHandlerClass</param-name> <param-value>com.github.tomakehurst.wiremock.http.StubRequestHandler</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>wiremock-mock-service-handler-servlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>   <servlet> <servlet-name>wiremock-admin-handler-servlet</servlet-name>
<servlet-class>com.github.tomakehurst.wiremock.jetty6. Jetty6HandlerDispatchingServlet</servlet-class> <init-param> <param-name>RequestHandlerClass</param-name> <param-value>com.github.tomakehurst.wiremock.http.AdminRequestHandler</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>wiremock-admin-handler-servlet</servlet-name> <url-pattern>/__admin/*</url-pattern> </servlet-mapping>    <welcome-file-list> <welcome-file>index.json</welcome-file> <welcome-file>index.xml</welcome-file> <welcome-file>index.html</welcome-file> <welcome-file>index.txt</welcome-file> </welcome-file-list> <mime-mapping> <extension>json</extension> <mime-type>application/json</mime-type> </mime-mapping> <mime-mapping> <extension>xml</extension> <mime-type>application/xml</mime-type> </mime-mapping> <mime-mapping> <extension>html</extension> <mime-type>text/html</mime-type> </mime-mapping> <mime-mapping> <extension>txt</extension> <mime-type>text/plain</mime-type> </mime-mapping>
</web-app> 

web.xml的这项配置可以改变源文件位置

<context-param> <param-name>WireMockFileSourceRoot</param-name> <param-value>/WEB-INF/wiremock</param-value> </context-param> 

使用Maven管理依赖,配置如下:

<dependency><groupId>com.github.tomakehurst</groupId><artifactId>wiremock</artifactId><version>1.53</version><!-- Include everything below here if you have dependency conflicts --><classifier>standalone</classifier><exclusions><exclusion><groupId>org.mortbay.jetty</groupId><artifactId>jetty</artifactId></exclusion><exclusion><groupId>com.google.guava</groupId><artifactId>guava</artifactId></exclusion><exclusion><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId></exclusion><exclusion><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-annotations</artifactId></exclusion><exclusion><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></exclusion><exclusion><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></exclusion><exclusion><groupId>org.skyscreamer</groupId><artifactId>jsonassert</artifactId></exclusion><exclusion><groupId>xmlunit</groupId><artifactId>xmlunit</artifactId></exclusion><exclusion><groupId>com.jayway.jsonpath</groupId><artifactId>json-path</artifactId></exclusion><exclusion><groupId>net.sf.jopt-simple</groupId><artifactId>jopt-simple</artifactId></exclusion></exclusions></dependency>

具体的部署这里就不介绍了,说说WireMock的配置:

Ø WireMock的文件目录

如图所示:

mappings:存放映射描述的文件

__files:存放映射匹配结果的文件

图-4-WireMock的文件目录

WireMock的匹配规则示例

分两种:完整Url匹配和正则UrlPattern

Url:完全匹配

mappings:cities-mapping.json

{        "request": {                                    "method": "GET",                        "url": "/cities"},                                      "response": {                                   "status": 200,                          "bodyFileName": "/cities.json","headers": {"Content-Type": "application/json","Cache-Control": "max-age=86400"}}
}

__files:cities.json

{"cityName": "公司操作间","shortname": "WS","provinceName": "北京","provinceNameEn": "BeiJing City","code": "0001","cityNameEn": "Workshop"}

UrlPattern:正则匹配任何6位数的,例如:/customer/123456/

mappings:cities-mapping.json

{        "request": {                                    "method": "GET",                        "urlPattern": "/customer/[0-9]{6}/"},                                      "response": {                                   "status": 200,                          "bodyFileName": "/customer.json","headers": {"Content-Type": "application/json","Cache-Control": "max-age=86400"}}
}

__files:customer.json

{"channels": [],"code": "781","companyName": "","enable": true,"name": "163","password": "CC@ne.com","userState": "COMMERCIAL"
}

自给自足:平台化

使用WireMock通过mappings和__files文件夹可以有效管理映射和返回内容文件,但是所有文件的有部分可抽取未固定模板,而这些部分目前是手动编辑,关注这些部分会分散业务的精力,如果可以做成平台化管理,所有接口通过创建完成,文件命名规则全部由系统进行管理,将节省的时间更多投入业务关注和及早进行自测,这样子的收益将会更大。

那怎么样的平台才算能够满足当前需求呢?

  • 基于HTTP协议
  • 支持Url、UrlPattern匹配
  • 支持数据存储
  • API接口规范化管理
  • 提交表单即可生成mapping和__files所需文件
  • 不同项目接口有不同的前缀
  • 能够返回指定格式(json|xml|文本)内容

图-4-ServerMock-v1.0-架构图

根据架构图,做了总体规划如下:

图-5-ServerMock-v1.0规划

技术选型

由于原来的测试平台使用Python编写,为了保持风格一致,从界面录入到文件生成处理依然采用Python,后台工具使用WireMock的standalone模式,通过shell脚本进行一键启停管理,以及实时刷新url、mapping映射;

HTTP API Mock项目管理Web前台

使用Python+Django+MySQL进行开发,分为项目配置和接口配置两大部分。

项目配置页

介绍:配置协议、进行mock服务器的重启、重新加载(有新的接口文件生成系统会自动reset即可,当然手工reset也可以,即时加载无须重启服务等待)。

图-6-项目配置页

接口列表页

介绍:展示列表,列出相关URL、方法、是否正则、返回码、返回类型。

图-7-接口列表页

接口配置页

介绍:选择方法、URL类型,填写URL(如果选择URL类型为UrlPattern,则填写正则表达式),填写状态码、返回接口,以及返回头,就可以完成一个mock接口的创建。

图-8-接口配置页

接口配置有三种输入形式:

直接输入返回结果

图-9-手工输入

一般场景在返回结果500k以内的内容,可以直接输入,保存进入数据库;

通过url抓取返回结果

图-10-url抓取

一般场景在返回结果超过500k以上内容,目标Mock接口已经存在,可以直接抓取生成文件;

通过文件上传返回结果

图-11-上传文件

一般场景在返回结果比较大|目标Mock接口还未开发完成,手工上传返回内容的文件即可。

以上三种灵活的保存返回内容方式,最终保存的接口会按照以下格式生成mapping和__files所需文件:

图-12-mapping和__files文件格式

Mock项目管理Server后台

使用Java-WireMock进行后台服务,在项目配置页通过按钮:重启、重新加载,调用后台脚本:wiremock_controller.sh,仅供参考:

#!/bin/bash
if [ "$#" = 0 ];thenecho "Usage: $0 (start|stop|restart|reset)"exit 1
fidirWiremock=`pwd`
getCount=`ps -ef | grep "wiremock-1.53-standalone" | grep -v "grep" |wc -l`
wiremock_jar=${dirWiremock}/wiremock-1.53-standalone.jar
port=9999
wiremock_url=http://localhost:${port}stop(){count=${getCount}if [ 1==${count} ];thencurl -d log=aaa ${wiremock_url}/__admin/shutdownecho "Stop success!......"elseecho "Already stop"fi
}start(){count=${getCount}if [ 0==${count} ];thennohup java -jar ${wiremock_jar} --verbose=true --port=${port} &    echo "Start success!......"elseecho "Already start"fi
}if [ "$1" = "restart" ];thencount=${getCount}if [ 1==${count} ];thenecho "Wiremock is running,wait for restarting! ...."stop  echo "Start wiremock......"start  elsestartfielif [ "$1" = "start" ];thenecho "Start wiremock......"startelif [ "$1" = "stop" ];thenecho "Stop wiremock......"stopelif [ "$1" = "reset" ];thencount=${getCount}if [ 0==${count} ];thenecho "Wiremock must be running before reset,wait for starting! ...."startficurl -d log=aaa  ${wiremock_url}/__admin/mappings/resetecho "Reset success!......"
fi

其中:

“nohup java -jar ${wiremock_jar} --verbose=true --port=${port} &”:在linux系统后台运行WireMock;

“curl -d log=aaa ${wiremock_url}/__admin/mappings/reset”:是通过发送POST请求,重新加载新生成的配置文件,在WireMock的源码中可以看到:reset的作用:

public interface Admin {void addStubMapping(StubMapping stubMapping);ListStubMappingsResult listAllStubMappings();void saveMappings();void resetMappings();void resetScenarios();void resetToDefaultMappings();VerificationResult countRequestsMatching(RequestPattern requestPattern);FindRequestsResult findRequestsMatching(RequestPattern requestPattern);void updateGlobalSettings(GlobalSettings settings);void addSocketAcceptDelay(RequestDelaySpec spec);void shutdownServer();
}

通过一系列源码追溯,可以找到重置:

@Overridepublic void reset() {mappings.clear();scenarioMap.clear();}

可以推测映射文件是存放到列表的:

public class SortedConcurrentMappingSet implements Iterable<StubMapping>{private AtomicLong insertionCount;private ConcurrentSkipListSet<StubMapping> mappingSet;
......
}

当WireMock启动,日志有以下描述:

2015-02-12 11:38:37.844 Verbose logging enabled
2015-02-12 11:38:38.657:INFO::Logging to STDERR via wiremock.org.mortbay.log.StdErrLog
2015-02-12 11:38:38.664 Verbose logging enabled/$$      /$$ /$$                     /$$      /$$                     /$$
| $$  /$ | $$|__/                    | $$$    /$$$                    | $$
| $$ /$$$| $$ /$$  /$$$$$$   /$$$$$$ | $$$$  /$$$$  /$$$$$$   /$$$$$$$| $$   /$$
| $$/$$ $$ $$| $$ /$$__  $$ /$$__  $$| $$ $$/$$ $$ /$$__  $$ /$$_____/| $$  /$$/
| $$$$_  $$$$| $$| $$  __/| $$$$$$$$| $$  $$$| $$| $$   $$| $$      | $$$$$$/
| $$$/   $$$| $$| $$      | $$_____/| $$  $ | $$| $$  | $$| $$      | $$_  $$
| $$/     $$| $$| $$      |  $$$$$$$| $$ /  | $$|  $$$$$$/|  $$$$$$$| $$   $$
|__/     __/|__/|__/       _______/|__/     |__/ ______/  _______/|__/  __/port:                         9999
enable-browser-proxying:      false
no-request-journal:           false
verbose:                      true

图-13-WireMock启动

成功处理请求的日志:

2015-02-12 11:41:10.320 Received request: GET /test/today/dkfDF123/1234/ HTTP/1.1
Host: 192.168.32.55:9999
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: csrftoken=alXbvCtMyTBI1wnSnRoljguTaBnTDbPo; sessionid=tvoi9rzs66umnt1a26wsj36eqry2e2lo
Connection: keep-alive

总结

HTTP API接口测试痛点是什么?很多公司划分不同研发组,各组系统之间的数据交互通过接口来实现,那很多时候就是集中在接口开发不同步,测试无法及早参与,对接调试难的问题。或许很多团队遇到这种问题,就是选择同步开发或者等待。当你选择等待的时候,你的产品质量就得不到及时验证,因为根本没有测试过,在当前快速迭代的开发模式中,时间是最致命的要素,如果不能及时交付,交付的质量又得不到保证,那是相当被动的局面,最后返工的成本比你当时愿意追加测试的成本会来的更高。

遇到这类问题是想办法解决,而不是回避,我们可以借鉴《自动化单元测试实践之路》在单元测试中,使用Mockito对依赖进行Mock,那同样道理,使用Mock技术也可以对HTTP API进行Mock,按照这个思路探索下去,看看有没有开源解决方案,是否能够解决当前问题,如果可以就不用重复写一套解决方案;如果不行,那能否基于开源的做二次开发呢?当团队经历过测试痛点,调研收集了一定的数据,这些问题的答案就会浮出水面了。

或许有人要问,使用之后能够提高多少效率呢?看回《图-2-改进的接口对接调试流程》,根据我们的经验,要统计当前迭代中有多少API需要对接调试,如果对比旧的模式来说,API接口调试效率提升至少有10%;可想而知,迭代中全是依赖API接口开发的话,那提升的效率就相当可贵了。

HTTPServerMock从手工到平台的演变相关推荐

  1. HTTP Server Mock 从手工到平台的演变(二)

    2019独角兽企业重金招聘Python工程师标准>>> 大家都知道,不管是 Web 系统.还是移动 APP,各自在与内部.外部系统之间进行数据交互时,大多数情况下都是依赖接口.在基于 ...

  2. HTTP API 自动化测试从手工测试到平台的演变

    不管是 Web 系统,还是移动 APP,前后端逻辑的分离设计已经是常态化,相互之间通过 API 调用进行数据交互.在基于 API 约定的开发模式下,如何加速请求 / 响应的 API 测试,让研发人员及 ...

  3. 阿里巴巴电商平台架构演变之路

    作者:子柳,唐三(云栖社区) 阿里已经不单单有电商业务,今天我们涉猎的非常广泛,布局也非常多.阿里从一家电商公司开始,如果业务已经覆盖到了各个行业,图为4年前,2015年的布局. 按照这样的业务发展速 ...

  4. 【SDCC 2016现场】互联网应用架构实战峰会(上):蚂蚁金服、宅米、携程、腾讯平台架构演变与实践...

    摘要:2016年3月18-19日,由CSDN重磅打造的"2016中国软件开发者大会"在上海光大会展中心国际大酒店隆重召开.在第二天的架构实践论坛上,来自蚂蚁金服.宅米网.携程旅游网 ...

  5. 拥有一亿会员的爱奇艺如何搭建大数据实时分析平台

    生活在信息爆炸时代的我们越来越清晰的认识到海量信息与数据分析的重要性,如提高数据挖掘能力.为运营决策提供关键数据.通过数据分析助力业务创新.在商业决策中的提供较有价值的信息等成为关键,于是大数据分析平 ...

  6. Steve Thair谈DevOps on Windows的演变与面临的挑战

    \ 本文要点 \\ 借助服务器角色的日益模块化.没有GUI的Server Core版本以及Powershell DSC的出现,几经演变,Windows服务器平台已经支持DevOps:\\t 每个人都需 ...

  7. 爱奇艺大数据实时分析平台的建设与实践

    0 导语 生活在信息爆炸时代的我们越来越清晰的认识到海量信息与数据分析的重要性,如提高数据挖掘能力.为运营决策提供关键数据.通过数据分析助力业务创新.在商业决策中的提供较有价值的信息等成为关键,于是大 ...

  8. 细看10个不同类型的社交平台

    无兄弟不社交:细看10个不同类型的社交平台 2012-08-06 14:03 | 1536次阅读 | 来源:thenextweb[已有1条评论]发表评论 关键词:社交,商业,平台,微博,产品 | 作者 ...

  9. 怎么注册微信公众平台

    微信公众平台是面向政府.名人.企业.媒体等机构推出的合作推广业务.在这里可以通过微信渠道将品牌.企业信息推送给上亿的微信用户.减少宣传成本,提高品牌知名度,打造更具影响力的企业形象.同时,随着微信用户 ...

最新文章

  1. 网络编程学习笔记(tcp_listen函数)
  2. sqlserver 两表联查去重_去山东省(烟台)必吃“特色”小吃 ,舌尖5大美食享受!...
  3. 2019,顺丰不顺风
  4. android app.build文件_网易友品 Android 客户端组件化演进
  5. 验证内容是否为空的多种办法 1210 c#
  6. 【Java】环境变量配置
  7. Luogu4114 Qtree1
  8. 实现百战铁路售票系统临时车次的添加功能
  9. 不要上网更新计算机系统的补丁程序,360安全卫士更新补丁后系统不能联网怎么办...
  10. 拆分器控件Splitcontainer
  11. 2020浙江大学软件学院软件工程考研经验分享
  12. 农夫山泉溜到了下坡路
  13. C++如何写adaptable仿函数
  14. 施密特正交化过程编程c语言,利用C程序编写格拉姆-施密特正交化的过程.docx
  15. thinkpade450装内存条_Thinkpad e450c我想加一个内存条,因为开机就满了百分50左右,该加什么样的内存条?低电...
  16. java全角数字_JAVA技巧(JAVA全角和半角的转换代码)
  17. mysql保留小数点后一位 进位处理_请问EXCEL保留小数点后一位时是如何修约的?
  18. CUDA:使用CUFFT来合成和 实时渲染海洋表面实例
  19. 【笔记|C++】最大公约数、最小公倍数的四种求法
  20. STM32 串口的使用

热门文章

  1. CSS3图片阴影效果解析
  2. 2020年中国5G基站建设行业报告.pdf (附下载)
  3. 从Palantir上市看智能决策平台发展前景
  4. 如何升级Jenkins版本
  5. 歪解:备份BlackBerry手机上面的安装好的程序(cod)
  6. 计算机专业退休有退休金,那些专业有退休金 哪个行业的退休金最多?
  7. 基于SL773-2018计算土壤流失量的Python实现
  8. unix环境高级编程之 read与write 函数详解
  9. 轨道交通行业网站(持续完善)
  10. grabcad无法注册也能下载模型的方法