127 lines
11 KiB
Markdown
127 lines
11 KiB
Markdown
# 05 | 案例:测试框架如何才能支持RESTful风格的接口?
|
||
|
||
你好,我是陈磊。
|
||
|
||
在前面的课程中,我们一起学习了如何把流程化的测试脚本,一步一步抽象成你自己的测试框架。无论你用的是什么编程语言,封装和抽象自己的测试框架都会让你的接口测试任务事半功倍。
|
||
|
||
我相信你在平时生活或工作中,一定会接触到各式各样的软件系统,而现在的软件系统和5年前相比,最大差别就在于结构不同。
|
||
|
||
在我读大学的时候,绝大部分系统还都是用一个Tomcat来搞定的;但现在的系统更加复杂,它们已经无法只用一个Web中间件独立对外提供服务,它们之间都也是通过相互调用来完成业务逻辑的,这里面既包含了服务端和服务端的调用,也包含了前端和服务端的调用,这就催生了RESTful风格的HTTP接口。
|
||
|
||
所以,这节课我就来和你讲讲,如何让你的测试框架完美支持RESTful风格的接口测试。这里我希望你能不断强化封装测试框架的三个流程,不断为自己的接口测试框架添砖加瓦。
|
||
|
||
不过,我不会将RESTful的规则一条一条念给你听,我想让你知道的重点是作为测试工程师,你要学会从测试工程师的角度观察RESTful接口,要学会怎么分析和验证这类接口,这也是今天我们今天这节课的主要内容。
|
||
|
||
## RESTful风格接口关我什么事?
|
||
|
||
看到这里,你是不是一脸困惑:**RESTful是一个接口的封装风格,和我们测试人员有什么关系呢?**
|
||
|
||
要想理解它和我们测试工程师的关系,你就要先知道RESTful风格的接口到底有什么好。
|
||
|
||
如果你用螺丝、钉子和板材等一系列原材料组装过家具,那么你肯定见到过各种千奇百怪的螺丝,比如一字的、十字的、三角形的、六角形的和海星形的等等,为了加固这些各式各样的螺丝,你就要准备各式各样的螺丝刀,因此,你的工具箱就会被不同规格和大小的螺丝刀填满。
|
||
|
||
不知道你是不是也和我一样,面对塞满螺丝刀的、乱七八糟的工具箱,心里非常急躁。但后来我在宜家看到一款螺丝刀,它只有一个刀柄,但给你提供了一整套各种形状、各种大小的螺丝刀刀头。
|
||
|
||
这样你在使用时,只要根据螺丝规格的不同,选择替换同形状的刀头就可以了;与此同时,它们放在工具箱里面又会显得很整齐,而不会七零八落。而且,如果你后续需要使用其它特殊形状的螺丝刀,你只要买和刀柄连接口一样的刀头就可以了,而不用再买一个完整的螺丝刀了。
|
||
|
||
如果你理解了上面这个场景,也就能很好地理解RESTful风格的接口了。**它主要就是一组设计原则和约束条件,本质上就是让消费者依据URI就可以找到资源,并通过简单的服务输入输出完成服务的交互。**
|
||
|
||
它所约束的每一个URI,都是独一无二的一个资源,通过HTTP的方法进行资源操作,实现表现层的状态转化。这就和螺丝刀刀头一样,待解决的问题就像螺丝,每一个接口只面向一种特定的资源,而不需要关心其他接口的处理方式,这样,你就能够一目了然地知道,该用哪种螺丝刀头拧哪种螺丝了,这就降低了接口开发的复杂度。
|
||
|
||
软件开发人员只要遵循RESTful的实践标准,按照一定的内部定义开发外接口,就会形成类似螺丝刀刀头一样轻便的接口,对外提供服务。现在的很多项目,无论是服务端和服务端的调用,还是前端和服务端的调用,都采用了这一种方式来设计接口。
|
||
|
||
对于我们测试工程师来说,RESTful风格的接口还是之前的访问模式,它同样是一种HTTP协议的接口,同样可以使用我们上节课一起封装的框架完成接口测试的任务。
|
||
|
||
但是,它和我们之前讲过的HTTP协议的接口测试还是有一些区别,这些区别导致了你现在的框架还需要做一些修改,这样才能让它支持RESTful风格的接口测试。
|
||
|
||
## 让你的框架可以测试一个RESTful风格接口
|
||
|
||
现在,你知道RESTful接口和你的接口测试有很大关系了,那么,RESTful接口的测试和原始的HTTP协议接口的测试,又有什么区别呢?
|
||
|
||
这里面有两部分需要你特别关注:**数据交换的承载方式和操作方式**。
|
||
|
||
我先说说数据交换的承载方式,RESTful风格的接口主要是以JSON格式来进行数据交换,如果你还记得我之前提过的“Battle”这个系统(你可以回到[03](https://time.geekbang.org/column/article/193912)中查看它),那么你一定在它的Readme.md中,看到了Request和Response对数据部分的一些定义,那就是JSON。虽然“战场”不能算是一个严格的RESTful接口的系统,但是,在数据交接的承载方式上,它模仿了RESTful的样子。
|
||
|
||
另外一个部分是操作方式,在“战场”系统中,我们用了HTTP协议的Get和Post,其实HTTP协议有很多方法,但是我们仅仅用了这两种,而RESTful的规定,使HTTP的很多方法都被利用到了,比如说,Get方法用来获取资源,Post方法用来新建资源(或者更新资源);再比如说,Put方法用来更新资源、Delete方法用来删除资源等等。
|
||
|
||
在明白RESTful风格的接口和普通的HTTP协议接口的区别后,我们现在来想一想,你自己的框架需要添加什么内容,才能支持RESTful风格的接口呢?**这里我总结了两个方法:借助外力和自己封装。**
|
||
|
||
### 借助外力
|
||
|
||
这里,我们RESTful的第一个数据交换的承载方式是JSON,我们的框架在之前的“战场”系统中就已经使用了它,虽然全部的操作都是参数拼凑的过程,但这已经满足了我们的需求。
|
||
|
||
这时如果你仍要拼凑很多复杂的数据,就需要使用JSON字符串和代码对象实体的转换,它有一个专业的叫法:**序列化和反序列化**。这个词语听着就很难理解,所以现在,我用一个生活中的小例子来告诉你,这个晦涩难懂的概念到底是什么意思。
|
||
|
||
如果你在商场看中了一款衣柜,但它很大,为了方便运输,就必须要先把它拆掉,运到家后再重新组装。你和商家协商好了,由他们为你把这个衣柜拆成可重组的零件运到家里,然后由你自己把这些零件重新组装成一个衣柜。
|
||
|
||
那么在这里,商家把衣柜拆成各个零件、然后打包的这个过程就是“序列化”,在代码中,就是将一些程序对象转换成JSON等格式的字符串的过程。接下来,你用这些零件再重新组装成一个衣柜,这个过程就是“反序列化”,在代码中,就是JSON等格式的字符串转换成程序的对象的过程。
|
||
|
||
为了能让你的框架可以快速完成序列化和反序列化,我建议你在代码中引入一个外部支持的库,就像Python有JSON库、Java有Fastjson库。这些公开的库其实都不需要做任何的修改,就可以拿来使用,所以,无论你使用哪种技术栈,这样的基础库都是存在的,你只需要在网上找一下,然后花几分钟看一下怎么使用,就可以拿到自己的框架里使用了。
|
||
|
||
### 自己封装
|
||
|
||
现在,我们已经可以借助开源库,解决数据交换的事情了,但是,RESTful风格接口和普通HTTP接口相比,还有一个明显的区别,那就是RESTful规定了HTTP的每一个方法都做固定的事情,可我们原来框架中的Common类却只支持Get和Post方法,因此,你需要在Common类中加入Delete和Put方法的支持。具体的操作你可以依据下面这个代码段来完成:
|
||
|
||
```
|
||
def put(self,uri,params=None):
|
||
'''
|
||
封装你自己的put方法,uri是访问路由,params是put请求需要传递的参数,如果没有参数这里为空
|
||
:param uri: 访问路由
|
||
:param params: 传递参数,string类型,默认为None
|
||
:return: 此次访问的response
|
||
'''
|
||
url = self.url_root+uri
|
||
if params is not None:
|
||
# 如果有参数,那么通过put方式访问对应的url,并将参数赋值给requests.put默认参数data
|
||
# 返回request的Response结果,类型为requests的Response类型
|
||
res = requests.put(url, data=params)
|
||
else:
|
||
|
||
# 如果无参数,访问方式如下
|
||
|
||
# 返回request的Response结果,类型为requests的Response类型
|
||
|
||
res = requests.put(url)
|
||
|
||
return res
|
||
|
||
|
||
def delete(self,uri,params=None):
|
||
'''
|
||
封装你自己的delete方法,uri是访问路由,params是delete请求需要传递的参数,如果没有参数这里为空
|
||
:param uri: 访问路由
|
||
:param params: 传递参数,string类型,默认为None
|
||
:return: 此次访问的response
|
||
'''
|
||
url = self.url_root + uri
|
||
if params is not None:
|
||
# 如果有参数,那么通过delete方式访问对应的url,并将参数赋值给requests.delete默认参数data
|
||
# 返回request的Response结果,类型为requests的Response类型
|
||
res = requests.delete(url, data=params)
|
||
else:
|
||
# 如果无参数,访问方式如下
|
||
# 返回request的Response结果,类型为requests的Response类型
|
||
res = requests.delete(url)
|
||
return res
|
||
|
||
```
|
||
|
||
在上面的代码中,你可以看到,我们为了实现HTTP协议的Put和Delete方法,自己封装了put()函数和delete()函数。其实,要实现RESTful风格的接口测试,你只要封装HTTP协议对应的Method方法就可以了,这样,你的框架就能完美的支持RESTful风格的接口了。完成了这个操作后,我们的Common类就既可以完成HTTP协议接口的测试,也可以完成RESTful接口的测试了。
|
||
|
||
## 总结
|
||
|
||
到这里,我们已经结束了今天的课程了。我们今天主要完成了RESTful风格接口的测试,对比之前的例子以及你自己的测试框架,针对框架中RESTful里缺失的部分,我为你提供了对应的解决方法。
|
||
|
||
在文中我讲了很多内容,但是完成RESTful风格接口测试,主要是通过两步操作,来为你的测试框架添加对应接口的测试能力的:
|
||
|
||
1. 借助外力。目前网上已经有很多成熟的、各式各样的支持库,你要尽量拿来为己所用,而不要从零建设,这样,既弥补了我们开发能力不强的短板,也能提高我们的研发效率。
|
||
2. 自己封装。你要注意的是,自己封装和借助外力并不互相冲突,你要借助外力,然后将它封装到你自己的框架中,这是一个借力打力的好方法。
|
||
|
||
随着我们的课程的不断深入以及内容的不断丰富,我相信,你最终会获得一个完全适合你自己,又可以解决实际工作任务的测试框架,这也是你自己的接口测试武器仓库,里面有解决各种接口测试问题的方法。它会是一个私有仓库,里面每一个武器都是为你自己量身定制的,因此,每一件武器你用起来都会更得心应手。
|
||
|
||
## 思考题
|
||
|
||
我今天讲了RESTful接口测试,并为你的私有测试框架添加了各式各样的新武器,那么,你能用你现在的新武器,解决一个你负责的RESTful的接口测试吗?在今天的框架中,随着你实际工作的使用,你又有了什么样的新设计呢?
|
||
|
||
我是陈磊,欢迎你在留言区留言分享你的观点,如果这篇文章让你有新的启发,也欢迎你把文章分享给你的朋友,我们一起探讨和学习。
|
||
|