写点什么

自动化测试基础设施(一)——为功能测试构建通用 mock server 系统

  • 2013-05-18
  • 本文字数:3371 字

    阅读完需:约 11 分钟

mock 在单元测试中已经众所周知。现今我们有各种功能强大而又好用的 mock 框架,可以很方便的解除单元测试中各种依赖,这大大的降低了编写单元测试的难度。而测试驱动开发(TDD)更进一步将 mock 作为一种设计手段,来辅助识别出元素之间交互的接口和职责。

那么在功能测试 (这里提到的功能测试指的是用户级测试) 这个层次,是否有必要使用 mock 呢?如果有必要又将如何构建呢?或者说是否有可能像单元测试中那样构建一个通用的 mock server 系统呢?本文将根据我的实践经历,向大家介绍一个通用 mock server 系统的主要组成部分以及设计思路。

Why

现今的业务系统很少孤立存在,它们或多或少需要使用兄弟团队或是其他公司提供的服务,这为我们的联调和测试造成了麻烦。对于这种情况,我们常见的解决方案是搭建一个临时的 server,模拟那些服务,提供数据进行联调和测试。这就是 mock server 的雏形。一般来讲,搭建这种 mock server 系统比较简单,不过它的功能也比较简单,而且往往需要针对不同的接口重复开发。那有没有可能像单元测试中使用的 mock 框架那样构建一个通用的 mock server 系统呢?

How

观察单元测试中的 mock 框架,我们会发现一般使用 mock 的流程是:

复制代码
init mock // 创建 mock 对象
config mock // 设置 mock 期望
setup mock // 将 mock 对象设置给被测对象
call // 调用被测接口,被测接口里的代码会调用 mock 对象
verify // 验证
拿 mockito 举例:
User expected = new User(“admin”, “12345”);
//init
UserDAO dao = mock(UserDAO.class);
//config
when(dao.findByName(“admin”)).thenReturn(expected);
//setup
UserService service = new UserService(dao);
//call
User actual = service.login(“admin”, “12345”);
//verify or assert

借鉴这种做法,我么就可以构建一个简单的 mock server 系统,接下来的内容中,我们会在这个 mock server 的基础上演化出比较完善的版本。

版本 1(简单的模拟值)

假设我们需要 mock 的是 HTTP 接口。我们的 mock server 提供一个配置接口 (对应着上面的 config mock 步骤),测试运行之前调用配置接口将需要 mock 的 HTTP 接口 URL 以及需要返回的值传递过去,mock server 内部建立 url 到返回值的关联 (这里就类似存在一个哈希表一样)。Mock server 还提供另外一个接口供被测系统调用。这是一个通用的接口,所有原先指向真实服务的地址全部被指向到该接口 (可以通过修改配置或修改系统 hosts 文件)。当该接口被调用时会寻找刚才建立的 url 到返回值的关联,并将 mock 的值返回。这样一个非常简单的 mock server 就构建出来了,对于一些简单的联调场景基本够用。这个时候我们的 mock server 的原理图如下面所示:

版本 2(提供调用参数的查询)

有了第一版的 mock server,对一些只需要模拟值的场景是够用了。但是,mock 的作用仅限于此么?想想单元测试中的 mock。在单元测试中 mock 框架除了能够为被测系统构建输入值外,还能捕获到被测程序传出的值,然后对这些值进行校验。举一个实际的例子:在我们的系统中经常需要向用户发送短信,为了对发送的短信功能以及短信内容进行验证,在没有 mock 之前我们可能真的需要向真实手机发送短信,然后验证。这不仅降低了测试效率,也增加了不少不可控因素。为此我给 mock server 添加第三个接口:query 接口。在被测系统调用 mock 接口时,mock 会记录下被测系统传递给 mock 接口的参数值,然后测试中可以调用 query 接口查询到记录的值,在测试中可以对其断言,而且这里记录下的调用记录还可以作为日志提供出来,提高系统的诊断能力。这样我们的 mock server 的结构就变成:

版本 3(可以根据参数模拟)

现在我们的 mock server 已经可以提供类似 verify 的功能了,但实际上它还不能算一个真正的 mock。它也不能处理哪怕稍微复杂一点的情况。在实际中,我们经常需要针对不同的参数返回不同的值。举个简单的例子:我们 mock 一个支付接口,对于订单号 123 我们期望支付成功,对于订单 234 期望支付失败。这就引入了我的 mock server 的两个核心组件:extractor 和 matcher。Extractor 组件主要用于从调用的参数上提取出参数值,然后转换成 key/value 的格式提供给后续的环节使用;因为请求的参数格式多种多样 (json,xml 等),extractor 为此提供了统一的接口。

Matcher 组件的作用是在拿到 extractor 传递过来的 key/value 值后利用一些匹配器匹配到具体设置的期望上,所有匹配到的调用会返回对应的值。也就是说 mock server 内部不再是简单的映射了。后面再介绍 DSL 部分的时候会介绍 matcher 使用的语法。这样我们的 mock server 结构就演化成下图:

版本 4(提供多种协议的支持)

估计有人在抱怨,说了这么多这个 mock server 还只能 mock HTTP 接口啊,我们的系统中存在 HTTP 接口,RPC 接口,SMTP 接口等等。这是 mock server 中协议组件的职责。协议组件是 mock server 的入口,它提供多种协议的服务,并且解析出协议包数据,然后将数据交给 extractor 组件;除此之外,协议组件在收到上层的返回值后,会按照协议的格式返回给被测系统 。利用一些开源的类库,我们可以很容易对一些通用协议提供支持,但对一些私有的二进制协议如果没有现成的库支持,要重新开发成本很大,不过我们可以从客户端来解决这个问题,这在后续的文章中会有介绍。

版本 5(模拟行为)

基本上一个功能还算完善的 mock server 成型了。但这就够了么?对于要模拟各种场景的测试还远远不够。我们很多接口有回调的功能,我们通常还需要模拟接口超时的情况,而对于一些支付相关的接口经常需要对参数进行加密解密,而且这些情况都需要是可配置的。有没有发现,前面我们介绍的所有实际上都是 mock 值。也就是我们设置一些值,然后调用的时候将值返回。但是很多时候我们不仅需要 mock 值,更要 mock 行为。这样我们有了 mock server 中最核心的组件:命令执行引擎 (好牛的名字,其实就那样)。在设置 mock 的时候我们不再是设置一个值,而是设置一个预定义命令组合成的流水线 (即按照类似下面 xml 的配置一步一步执行,并且可以将上一步的执行结果传递给下一步):

复制代码
<delay>1000</delay>
<callback url=http://localhost/callback.do>{“ret”:”true”}</callback>
<return>{“ret”:”true”}</return>

上面的流水线被命令执行引擎解析执行后就是按顺序执行对应的 DelayCommand, CallbackCommand 以及 ReturnCommand 命令了,具体命令就不介绍了。采取这种方式给我们 mock server 带来了很大的灵活性:只需要简单的扩展一个子命令,就可以扩充 mock server 的行为。比如 mock 某网关接口时需要使用 MD5 加密,只需要扩展一个 MD5Command(下面代码中的 $result 表示前一步骤 <md 5 /> 加密后的结果):

复制代码
<md5 />
<return>$result</return>

DSL

现在我们的 mock 不仅可以 mock 值了,对于各种行为的模拟也得心应手。但是要使用方便,还要提供便于使用的接口。Mock server 提供两类接口:针对自动化测试的 DSL,以及针对手工测试使用的管理界面。这里主要介绍这种 DSL(因为我们的测试用例是使用 xml 编写,转换成编程语言也很容易):

复制代码
<mock service=”http:/test.json” matcher=”hasKey($param.orderNo)”>
<delay>1000</delay>
<md5 />
<return>$result</result>
</mock>

service 是对被 mock 的服务的描述,比如对于 SMTP,我们可以这样定义: service=“smtp:9000”。这个表示在 9000 端口上监听 smtp 协议。而 matcher 即前面介绍的 matcher 组件所使用的各种匹配器,用于匹配被测系统调用 mock server 时传递的数据。比如上面的例子表示的就是如果被测系统调用 http 接口 /ticket.jsp,并且参数里包含 orderNo 则延迟 1 秒钟,然后返回一个 json 值 。

总结

前面几节介绍了一个比较完善的通用 mock server 从简到繁演化的设计思路,希望可以为想要构建类似设施的读者提供一个参照。

这个 mock 系统包含两个主要部分:mock admin 和 mock server。Mock admin 是管理界面,主要提供监控 (可以在界面上实时看到被测系统与 mock server 交互) 以及手工测试时的配置界面。 Mock server 即前面介绍的主体,其架构如上图所示。Mock server 包含几个核心组件:协议、extractor、matcher、命令执行引擎、存储 (即 mock server 中使用的各种数据的存储)。Mock server 提供三类接口:配置、被 mock 接口 (各种服务,通过协议组件提供)、查询。

2013-05-18 05:1510854

评论

发布
暂无评论
发现更多内容

精益产品开发体系最佳实践及原则

阿里云云效

云计算 阿里云 精益开发 产品开发 开发

技术干货 | Linkis实践:新引擎实现流程解析

康月牙

Apache 开源社区 WeDataSphere Linkis 使用实践

ARM64 上的性能怪兽:API 网关 Apache APISIX 在 AWS Graviton3 上的安装和性能测试

API7.ai 技术团队

AWS 网关 arm APISIX

数据安全刻不容缓,国产智能化厂商首获SOC 2鉴证报告有何意义?

王吉伟频道

RPA 数据安全 机器人流程自动化 智能自动化 SOC 2

《数字经济全景白皮书》银行财富管理篇 重磅发布

易观分析

理财 银行理财

知名网络安全硬件平台厂商铵泰克加入龙蜥社区

OpenAnolis小助手

开源 网络安全 龙蜥社区 CLA 铵泰克

细说腾讯如何做到直播延时降低90%以上方案

C++后台开发

WebRTC CDN 音视频开发 视频直播 直播低延迟

618 大促来袭,浅谈如何做好大促备战

阿里巴巴云原生

阿里云 微服务 高可用 云原生

为什么 SQL 语句使用了索引,但却还是慢查询?

okokabcd

MySQL

分布式数据对象:超级终端的"全局变量"

OpenHarmony开发者

OpenHarmony

工资管理系统该如何使用?

低代码小观

企业管理 工资 管理系统

企业数字化转型该如何做?三个融合、三个转换

小炮

数据产品学习-实时计算平台

第519区

实时计算 数据产品 数据开发 大数据平台

技术干货 | Linkis1.0.2安装及使用指南

康月牙

开源社区 微众银行 WeDataSphere Linkis 使用实践

后端适用,Apifox接口文档设计和调试教程【工具篇】

Liam

Java 后端 Postman 后端开发 API文档

defi存币生息理财dapp系统开发逻辑

开发微hkkf5566

Java 对象如何安全的 toString

HoneyMoose

Springcloud Oauth2 HA篇

Damon

微服务架构 安全架构 6月月更

陕西西安等保测评单位有哪些?在哪里可以查到?

行云管家

西安 等保测评 等保测评机构

网络安全等级测评和商用密码应用安全性评估是一回事吗?

行云管家

网络安全 等级保护 商用密码

资深OpenStacker - 彭博、Vexxhost升级为OpenInfra基金会黄金成员

Geek_2d6073

构建基于React18的电子表格程序

葡萄城技术团队

React 表格 纯前端表格技术

各厂商的数据湖解决方案

五分钟学大数据

数据湖 6月月更

直播预告 | 社交新纪元,共探元宇宙社交新体验

ZEGO即构

低代码分析盘点:银行业低代码应用需要规避两大误区

易观分析

代码 银行

喜报 | 旺链科技签约汨罗市文旅体产业项目,打造“链”上数字乡村

旺链科技

区块链 产业区块链 乡村振兴 汨罗市

元宇宙产业投资全景图,快人一步走进元宇宙新时代!

博文视点Broadview

Ubuntu20.04设置静态IP

echeverra

Linux 静态IP

大数据培训Flink高频面试题

@零度

flink 大数据开发

基于模板配置的数据可视化平台

百度Geek说

InfoQ 极客传媒 15 周年庆征文|在Flutter中自定义应用程序内键盘

坚果

InfoQ极客传媒15周年庆

自动化测试基础设施(一)——为功能测试构建通用mock server系统_软件工程_余昭辉_InfoQ精选文章