MockServer的测试思想与实现(下篇)

简介:

MockServer的技术实现 

目前,笔者已经用Python实现了一个基于socket接口的Mock Server并在测试中进行了一定的应用,实现中利用了一些Python的语言特性、一点RPC技术和一点DSL的技巧。 

一个CASE 

下面先看一个实际的CASE,CASE加入了许额外的注释,以解释这段代码的意义 (CASE的格式为一种可嵌入Python代码的DSL脚本)


CASE DS返回结果异常测试
BEGIN

GROUP_ID=55

__exec__=BEGIN
PYTHON mock_execute(r.strip())
PYTHON request(host='',port=${PORT},=${MEDIA_ID},=${GROUP_ID},time=${TIME},='',cached=${CACHED},cache_flag='')
END

CASE 测试 1
BEGIN

MOCK_DATA=BEGIN
Mock.on(
large_than(192),
).do(
send_back(
am_head_t(1,0,(c_uint32*2)(100,100),sizeof(ds_qres_head_t)+12+32),
ds_qres_head_t(0,1,1,32,12,),
2,4,
"show",
47,3,4,12,
123,"hello,world!"
),
clear_buf(),
clear_mock(),
)
END
END

CASE 测试 2
BEGIN
……
……
END
……
……
……
……
END


上面的代码中,request的函数的功能是将各参数拼装成一个HTTP请求发送给被测系统并接收返回的结果。而mock_execute也就是对 Mock Server的调用了。 

下面就看看这个Mock Server是如让上面的CASE得以运行的 

核心代码 

首先看Mock Server中的主体代码,该代码基于Python中的ThreadingTCPServer,如下: 

 

 
  
  self.stop   self.server.stop:
buf=self.sock.recv(4096)
buf:
time.sleep(0.1)
self.buffer+=buf
mock MockRequestHandler.mocks:
mock(self):


这段代码可以看作是程序的主循环,它不断的从socket读取代码,然后调用mock对象,触发其中定义的行为。一个mock对象定义了一个行为,程序充许一次定义的多个mock,也有使得程序可以模拟比较复杂的行为了。 

下面再看看mock对象里面是如何定义的 

 

 
 (
x,y:x y,
( f:f(handler),self.on_list),
True
):
'',self.on_list,self.do_list
( f:f(handler),self.do_list)
True
False


这里用到了一点儿函数式编程的技巧,在on_list中保存了当前mock的触发条件,do_list中则保存了当前mock要执行的操作,这段代码的就意思就是当所有的触发条件都满足时,就顺序执行操作列表的中的操作,否则就退出。 

上面两段代码就构成了Mock Server的核心逻辑。 

条件与结果 

接下来我们看看on_list和do_list里面到底是什么,以前面的CASE中用到的large_than和send_back为例,它们的原始定义如下所示: 

 

 
@mock_action
large_than(handler,size):

(handler.buffer)>=size


@mock_action
send_back(handler,data):

handler.sock.sendall(data)


可以看到,它们的第一参数都是handler,它代表了当前请求处理器的实例,包含以下几个基本的成员:
 

  • client_address 当前请求的客户端地址
  • sock 当前请求的socket对象
  • buffer 当前请求的数据缓冲区
  • stop 主程序停止标志

对照 核心代码 中的主循环,这里的handler就是循环中的self,这样就不难明白这几个成员的作用了。 

在on_list和do_list中保存就是对这些函数的“间接”引用。那什么是间接引用呢? 

回头看前面CAE中的代码: 

 

 
Mock.on(
large_than(192),
).do(
send_back(
am_head_t( ……
……


看以看到这里并没有给large_than和send_back传入handler参数,而且,熟悉Python语法的人也会发现,这是对函数的调用,而不是对函数对象本身的引用,最终on方法得到的参数应该是large_than执行完的结果,不是large_than这个函数对象。说了这么多,好像很混乱的样子,其实密秘就在

@mock_action

中,这个是python中的函数修饰器,它本身也是一个函数,接受一个函数对象为参数,返回一个新的函数对象,来看看mock_action的定义: 
 

 
 mock_action(f):

factor(*args):

action= h:f(h,*args)
action.__name__=f.__name__+''
action

factor.is_mock_action=True
factor.__name__=f.__name__+''
factor

它实际上对是原始的函数进行一次封装,有点类似函数式编程中的高阶函数,简单来说就是将开始那段函数定义的代码等价于下面的代码: 
 

 
 large_than(size):

large_than_func(handler):
(handler.buffer)>=size
large_than_func

send_back(data):

send_back_func(handler):
handler.sock.sendall(data)
send_back_func

当然也可以在定义Mock行为时写成这样: 
 

 
Mock.on(
handler:large_than(handler,192)
).do(
handler:send_back(handler, …… )

不过这个就有点儿太难看了。 

行为描述 

前面说了好多mock里存什么,现看看这此东西是怎么存进去的,来一段更有代表性的代码: 
 

 
Mock.on(
any_package(),
large_than(32),
).do(
send_back(''),
clear_buf(),
).on(
got(''),
).do(
close_sock(),
).on(
got(''),
).do(
stop_server(),
)

每一组on|do调用都定义了一个新的mock,上面的代码中定义了三个mock,那么如何能保证on|do能成对出现,且不符合约定时能抛出异常呢? 

其实上面的代码可看作是一段DSL的代码,我们在Python的语法基础上,添加了on/do两个关键字,并做了一定的语法限定。而在代码中实现on和 do时也进行相应的处理,以保证语法约定的正确性。 
 

 
 MockRequestHandler(SocketServer.BaseRequestHandler):

mocks=[]

MockObjectProxy(object):

(self,handle,obj):

self.__handle=handle
self.__obj=obj

do(self,*funcs):

self.__handle.mocks.append(self.__obj.do(*funcs))
self.__obj.on_list,self.__obj.do_list
self.__handle

@staticmethod
on(*funcs):

MockRequestHandler.MockObjectProxy(
MockRequestHandler,
MockObject().on(*funcs)
)


通过上面的代码就限制了do必须出现on后面,否则会提示Mock对象不支持do方法。同时如果只有on没有do,也不会创建新的mock。 

远程调用 


现在,我们的Mock Server已经可以启动运行了,但必须且只能在启动时指定mock行为,也就是说还不能动态更新配置。 

接下来就该RPC登场了,RPC是远程过程调用的简称,这里的远程指的是不同的进程,可能是同一台机器上的,也可能是位于不同机器上的,它们之间可以通过某种PIC(进程间通信)协议传递信息,比如socket。而RPC就是对PIC协议的再封装,把信息发送/接收的过程变成更简单易用的函数调用过程。 

本例中使用Python的第三方扩展库rpyc来实现RPC,这样就可以在CASE中动态的修改Mock Server的形为了。 
 

 
 execute (code,service_name=''):

conn=get_online_connectiones(service_name)[-1]
conn.root.execute(code)

=='':

execute(r)
()


在最开始的CASE中的mock_exectue函数,实际上就是对这里的execute的再包装而已。

综述 

总结一下我们所实现的这个Mock Server的特点: 

 

  • 用事件驱动的方式描述行为
  • 用函数来描述[条件]和[结果]
  • 用DSL代替配置文件的解析
  • 用RPC代替配置文件的分发和加载


相比之传统定义上的Stub Server, Mock Server抛弃了死板的配置文件,将要行为描述与接口实现分离,更利于代码的复用,进一步简化桩程序的开发成本。

作者:qabloger

(全文完)

 【本文首发于:百度测试技术空间http://hi.baidu.com/baiduqa/blog/item/450f19dc77915954ccbf1a4d.html











本文转自百度技术51CTO博客,原文链接:http://blog.51cto.com/baidutech/743677 ,如需转载请自行联系原作者

相关文章
|
5月前
|
项目管理
PMBOK泛读(开篇) - 整体思维导图
PMBOK泛读(开篇) - 整体思维导图
21 0
|
1月前
|
程序员 Python
类的设计奥秘:从代码到架构的科普全解
类的设计奥秘:从代码到架构的科普全解
13 2
|
9月前
|
设计模式 存储 JSON
如何写出一手好代码(上篇 - 理论储备)?
技术能力是研发同学的立身之本,而写代码的能力又是技术能力的重要体现。但可惜的是理想很丰满,现实很骨感。结合慕枫自己的经验来看,我们在工作中其实没那么容易可以看到写得很好的代码。
|
9月前
|
SQL 存储 缓存
系统设计不知怎么入手?一文帮到你
系统设计不知怎么入手?一文帮到你
78 2
|
11月前
|
存储 开发框架 前端开发
【系统设计】大神三分钟搞懂领域驱动设计(二)
【系统设计】大神三分钟搞懂领域驱动设计
|
11月前
|
前端开发 Java 程序员
【系统设计】大神三分钟搞懂领域驱动设计(一)
【系统设计】大神三分钟搞懂领域驱动设计
【系统设计】大神三分钟搞懂领域驱动设计(一)
|
11月前
|
存储 开发框架 数据可视化
【系统设计】大神三分钟搞懂领域驱动设计(三)
【系统设计】大神三分钟搞懂领域驱动设计
【系统设计】大神三分钟搞懂领域驱动设计(三)
|
SQL 安全 大数据
【基础理论-WEB测试】面试官让你介绍下web测试,标准的高大上回答如下:
【基础理论-WEB测试】面试官让你介绍下web测试,标准的高大上回答如下:
|
安全 UED
陪玩平台源码具有哪些优点,开发怎样实现
陪玩平台源码不仅实现陪玩下单、多人聊天等功能,还实现了用户直播、礼物打赏、快速匹配等功能,丰富系统功能,满足用户更多需求。
|
Java 数据库 NoSQL
也许能帮到你一点!eboot框架基础版发布
每次开发都从0开始,重复造了无数个轮子,或者是把之前的项目抠出来,改吧改吧。但是技术的更新速度太快,甚至还见过现在开发的项目用jdk1.6的,因为老项目用的1.6······ 为了简化项目开始这一步的操作,有了开发eboot的念头,已经有很多前辈开发过类似这样的东西,在这里先感谢一下走在前面的前辈们,但是每个人的都会有一些不同,能给大家提供多一种的选择也就足够了 eboot简介 目前发布一个基础版,适用于中小型项目开发,后续会加入更多功能,以满足大型项目的需求。
1244 0