BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

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

| 作者 余昭辉 关注 1 他的粉丝 发布于 2013年5月19日. 估计阅读时间: 12 分钟 | 如何结合区块链技术,帮助企业降本增效?让我们深度了解几个成功的案例。

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接口(各种服务,通过协议组件提供)、查询。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

并行的问题有麻烦 by 郑 鸿志

对于一些基础的模块,多个测试人员会用到,他们都在做测试,但是他们想要得到不同的结果,怎么办?

Re: 并行的问题有麻烦 by 余 昭辉

mock中有一个路由模块的,比如可以根据请求来源IP来区分。

技术选型的问题 by 吴 新鑫

hi, mock server能否给些技术选型的建议呢

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

3 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT