接口自动化方法
<p>编写人:卢可如
欢迎补充修改~</p>
<h3>一.问题</h3>
<h4>1.什么是接口自动化?</h4>
<p>接口自动化测试,其实就是让程序自动去测系统组件之间接口的一种测试。主要用于检测外部系统与系统之间,以及内部各个子系统之间的交互点。
接口测试的重点,是要检查前后台数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等;
主要测的是后台提供的功能有没有问题;后台对于前端发送的请求数据的各种场景的‘后台处理逻辑’是否有问题;和后台对于前端的请求的‘容错能力’怎么样;以及后台的接口/代码有没有对前端提交的数据做校验等。</p>
<h4>2.为什么要做接口自动化?</h4>
<p>个人觉得,是因为接口自动化成本比较低,不依赖于界面,实现起来比较容易。</p>
<h4>3.接口自动化怎么做?</h4>
<p>大致的流程(详见第二大点):
根据接口功能需求文档内容设计测试用例->把单个接口功能调通(JMeter去调试)->搭建测试环境->编写接口脚本->工程管理维护与优化</p>
<h4>4.接口自动化与WEB自动化的区别?</h4>
<p>我觉得主要区别如下:
(1)接口自动化没有界面,不需要元素定位,也不需要考虑页面延迟的问题,所以它的执行效率会更高一些;WEB自动化就恰恰相反;
(2)使用测试库也不一样,Web自动化用的框架是:python+selenium+unittest,接口自动化用框架是:python+requests+unittest;
(3)Web自动化最核心关键的部分是:元素定位和后期的工程管理维护与优化;
接口自动化最核心关键的部分是:1.参数的组织;2.发送请求;3.断言;另外,后期的工程管理维护与优化也是比较关键的;</p>
<h3>二.接口自动化的具体怎么做的?</h3>
<p>可以分为3个主要阶段,分别是测试前的准备阶段,脚本编写与调试阶段和工程的管理与维护阶段。</p>
<h4>1.测试前的准备阶段</h4>
<p>首先是要做好测试规划,比如说,先确定好哪些业务需要实现自动化,确定框架,搭建测试环境,准备数据/编写测试用例等。
比如,我之前做接口自动化用的是python+requests+unittest这个框架;
Requests:做基础脚本的开发;
Unittest:管理维护用例,统一加载,统一执行;
HTMLReport库/框架:生成图像化测试报告;
环境搭建:python3.7;安装 requests库;以及后期需要用到的一些库:paramunittest库;unittest库;pymysql库等;采取在线安装方式,比较方便 # pip install requests
测试用例:主要是根据之前写好的接口功能用例,来整理接口自动化的用例;并且,把接口自动化测试用例都放在Excel文件中,统一管理;这些都是要提前就做好的准备工作。</p>
<h4>2.脚本编写与调试阶段</h4>
<p>用requests这个测试库,具体操作的话,其实主要就是对一些请求参数的组织,如果有的接口需要传请求头的参数的话,就还需要组织请求头的参数(参数这块的话,都是以字典的格式来展示的);请求参数这一块,当请求参数是Json格式的,用data=params;如果不是Json格式的,要用params=params;一般GET请求用params的比较多;POST请求,参数格式是Json格式的比较多,所以用data=的比较多;
组织请求参数:
请求类型,一般就是GET和POST这两种类型;然后就是要获取响应的具体数据,并且根据获取的数据来做断言。
发送请求:
断言这一块,我主要是通过响应的状态码(常见的有200;302等);提示信息(常见的有OK;Found等)以及响应的正文内容来断言的;响应的正文内容,这个需要根据项目实际情况去定(比如说,错误码,提示信息,或者是其他的内容)等。另外数据库也要断言(根据数据库中具体的某个数据的前后状态断言)。</p>
<h4>3.工程的管理与维护阶段</h4>
<p>主要就是引入一个unittest框架(Python自带的单元测试框架),这个框架可以为我们统一加载和统一执行用例,具体操作的话,就是需要把脚本中的数据实现模块化和参数化。
模块化的目的是:通过把常用的公共业务模块封装起来,方面我们后期调用,这样做,可以提高代码的可重用性;
参数化的作用是:把脚本中的数据与脚本分离后,如果说在后期测试数据有变动的话,那么,我就只需要调整维护excel文件的数据即可,只需要在excel表格中进行增加,删除,修改操作,不需要去修改python脚本脚本代码,这样就很方便后期我们对脚本的维护;
最后就是引入报告,我是通过引用一个HTMLReport测试库(可以网上搜)就行了。
小结:
其实,我觉得,接口自动化里面最关键的是断言跟参数化这两点,我们把这两个点做好,基本上接口自动化问题就不会太大。</p>
<h3>三.具体的脚本编写方法</h3>
<h4>1.导入requests库</h4>
<h5>导包</h5>
<p>import requests</p>
<h4>2.参数的组织 #组织好URL,请求头,请求参数(参数一般通过字典来组织)</h4>
<p>url='<a href="http://v.juhe.cn/movie/index">http://v.juhe.cn/movie/index</a>'
params= {
'title': '复仇者联盟',
'key': 'a610816e9f9cf026634fb0e37b6a9f32'</p>
<h4>3.发请求 #调用requests库中的get,post方法,发请求,获取响应</h4>
<h5>后台返回的响应数据都放在对象r中,</h5>
<pre><code>r= requests.get(url,params,**kwargs)
r = requests.post(url,data,json,**kwargs)</code></pre>
<h6>有请求头需要传递请求头headers</h6>
<p>r = requests.post(url,data=params,headers=headers)</p>
<h4>4.获取响应数据</h4>
<h5>提取响应数据里面的内容,响应数据里面的内容很多,常见的如下(根据自己的需要选择打印查看获取的数据):</h5>
<pre><code>r.json()['error_code'] #正文内容中的错误码
r.status_code #响应状态码
r.reason #响应行中的提示信息 OK
r.raw #原始响应体,使用r.raw.read()读取
r.content #响应的具体内容,字节格式,需要进行解码,二进制的信息流
r.text #响应的具体内容,字符串格式,HTML网页(不能转json格式)/Unicode格式/文本等
r.json() #响应的具体内容,字典格式,request中内置的json解码器,转化成json格式的
r.raise_for_status() #请求失败(非200响应),抛出异常
r.url #获取请求的url
r.cookies #获取请求后的cookies
r.encoding #获取编码格式
r.request.headers #获取请求头
r. headers #获取响应头,以字典对象储存服务器响应头,但是这个字典比较特殊,</code></pre>
<p>字典键不区分大小写,若键不存在,则返回None</p>
<h4>5.断言:常见的断言方法是断言状态码、提示信息、响应正文内容的某个字段、数据库</h4>
<p>(1)状态码:#r.status_code #200
(2)提示信息:#r.reason #OK
(3)响应的正文内容:#r.tex #html网页
r.json() #文本
r.content #字节方式,需进行解码,二进制的信息流
(4)数据库 #根据数据库中具体的某个数据的前后状态断言
工程管理与维护的时候,可以使用unittest里面丰富的断言方法来断言,一般用关键字:assertEqual</p>
<h5>断言 '状态码/响应码'</h5>
<pre><code>self.assertEqual(res.status_code,200)</code></pre>
<h5>断言 '提示信息'</h5>
<pre><code>self.assertEqual(res.reason,'OK')</code></pre>
<h5>断言 '正文内容' #主要:错误码,提示信息,其他内容</h5>
<pre><code>self.assertEqual(res.json()['info'],self.assert_info)
self.assertIn(self.assert_info,r.text)</code></pre>
<p>例如:</p>
<pre><code>if response.status_code == 200 and response.reason=='OK' and '复仇者联盟'in response.text:
print('通过!')
else:
print('不通过!')</code></pre>
<h4>6.工程的维护与优化 & 批量执行用例</h4>
<p>(1)引入unittest框架,利用unittest框架管理各个业务功能模块的用例
(2)参数化:数据与脚本分离,方便后期的维护,数据有变动只需要改excel文件即可
(3)模块化:把常用的公共业务模块封装出来,方面后期调用,提高代码的可重用性
(4)工程架构(建工程)</p>
<pre><code>test_case:用来放用例脚本 以test开头,方便统一加载统一执行用例</code></pre>
<p><img src="http://rpddoc.weoa.com/server/../Public/Uploads/2019-08-19/5d5a5eb93cfb3.jpg" alt="" /></p>
<pre><code>data:用来放用例的数据**</code></pre>
<p><img src="http://rpddoc.weoa.com/server/../Public/Uploads/2019-08-19/5d5a5ee5c961d.jpg" alt="" /></p>
<pre><code>common:用来存放一些封装的公用的功能,比如:excel读取功能,数据库的访问功能,业务功能:比如登录功能;</code></pre>
<p><img src="http://rpddoc.weoa.com/server/../Public/Uploads/2019-08-19/5d5a5f34b9a7a.jpg" alt="" /></p>
<pre><code>config:存放的是一些全局变量,用于做参数化;
runall.py:执行所有用例的入口;</code></pre>
<h5>操作:</h5>
<p>1.定义一个类,继承unittest.TestCase,需要导包 import unittest ;</p>
<p>2.读取excel用例数据,封装一个函数读用例数据,利用paramunittest库来引用用例数据;
<img src="http://rpddoc.weoa.com/server/../Public/Uploads/2019-08-19/5d5a5f7854da8.jpg" alt="" /></p>
<p>3.实现/封装一个用例方法,YinPianjs(影片检索);在用例方法中去组织请求数据,发请求,做断言。
<img src="http://rpddoc.weoa.com/server/../Public/Uploads/2019-08-19/5d5a5f9a02862.jpg" alt="" /></p>
<h4>7.出报告</h4>
<p>(1)引入一个包 # HTMLReport
是一个单元测试测试运行器,可以将测试结果保存在 Html 文件中,用于人性化的结果显示。
仅支持Python 3.x</p>
<pre><code>HTMLReport官网:https://pypi.org/project/HTMLReport/
安装这个包:pip install HTMLReport
导包:# import HTMLReport</code></pre>
<p>(2)加载用例</p>
<pre><code>suite = unittest.defaultTestLoader.discover(testcaseFilePath,pattern='test_*.py')</code></pre>
<p>(3)用例用例执行器</p>
<pre><code>runner = HTMLReport.TestRunner(
report_file_name='test', #报告文件名,如果未赋值,将采用“test+时间戳”
output_path='report', #保存文件夹名,默认“report”
title='自动化测试报告', #报告标题,默认“测试报告”
description='fw接口自动化测试报告', #报告描述,默认“测试描述”
thread_count=1, #并发线程数量(无序执行测试),默认数量 1
thread_start_wait=3, #各线程启动延迟,默认0 s
sequential_execution=False, #是否按照套件添加(addTests)顺序执行,
#会等待一个addTests执行完成,再执行下一个,默认False
#如果用例中存在 tearDownClass ,建议设置为True,
#否则tearDownClass 将会在所有用例线程执行完后才会执行。
#lang='en'
lang='cn' # 支持中文与英文,默认中文)</code></pre>
<p>(4)执行测试用例套件</p>
<pre><code>runner.run(suite)</code></pre>
<p>(5)报告样板 <a href="https://liushilive.github.io/report/report/#cn">https://liushilive.github.io/report/report/#cn</a>
<img src="http://rpddoc.weoa.com/server/../Public/Uploads/2019-08-21/5d5d0e6b5cc67.png" alt="" /></p>
<h3>四.其他常见问题</h3>
<h4>1.Unittest实现接口自动化的效率高表现有哪些?</h4>
<p>接口自动化,直接调取unittest类中的API就可以实现,参数都放在Excel表中,通过Paramunittest自动提取Excel中的数据,做数据驱动即可;不需要像WEB端那样,受到界面/元素/时间等因素的影响;接口的话,实现起来比较简单,基于http协议的接口可以利用requests库进行设计测试,把请求发送出去,获取响应,对响应做处理就行;特别是json格式的,更好处理,直接可以检查状态码和返回数据。</p>
<p>注:使用unittest实现自动化的两个重点,用到两个对象:
1、unittest 2、Paramunittest</p>
<h4>2.接口自动化中cookie/session的处理(传递)与动态关联?</h4>
<h5>1 直接把登录模块封装,在后面用到的地方调用即可;</h5>
<h5>2 动态关联,登录->打印cookie值->通过‘请求头’把cookie值传给后台;</h5>
<p>当两个接口之间有关联的时候,也就是下一个接口的请求参数需要用到上一个接口中响应数据的cookie值,常见的比如说:查询,充值等接口都必须先要登录,说明这些接口用到了登录接口的响应数据中的cookie值,这个时候就需要先登录,然后获取登录的cookie值,然后再通过‘请求头’把cookie值传给后台;
具体方法的话,就是先发送一个登录请求,然后直接打印cookie值;再发送第二个接口请求的时候,把获得的cookie值,直接作为请求参数,通过‘请求头’传到后台即可;
方法一:</p>
<h1>发登录请求 取Cookies值的方法</h1>
<pre><code> res_login = requests.post(url,data=params,headers=headers)
print('cookies:',res_login.cookies)
#参数组织
Params={ }
headers={ }
#发充值请求
res2 = requests.post(url,data=params,headers=headers,cookies=res_login.cookies)
#断言
</code></pre>
<p>方法二:</p>
<h1>发登录请求</h1>
<pre><code> sess = requests.session()
res_login = sess.post(url,data=params,headers=headers)#这个请求没有用了,前面的登录请求会保存在sess中,在充值请求中直接用。
#发充值请求
res = sess.post(url,data=params,headers=headers)
print(res.text)</code></pre>
<p>方法三:
接口自动化的动态关联这块可以通过正则表达式去提取想要的参数,这里需要引用到re这个包里的findall函数(格式:对象 = re.findall(r'postid=(.+?)&'),r2.url)然后他会返回一个列表,我们定义一个对象去继承列表的值。引用时直接将取的对象的值直接放到下一个接口进行传参就行;
例如:
<img src="http://rpddoc.weoa.com/server/../Public/Uploads/2019-08-19/5d5a601501bc5.jpg" alt="" /></p>
<h4>3.转码问题</h4>
<p>有时候我们获取的值是Unicode格式的,显示的是乱码,如下:</p>
<pre><code>res = requests.post(url,data) #res.text为Unicode格式数据转化为utf-8格式的数据。</code></pre>
<p><img src="http://rpddoc.weoa.com/server/../Public/Uploads/2019-08-19/5d5a6061a57e7.jpg" alt="" /></p>
<p>需要转码成中文格式的,如下:</p>
<pre><code>print(res.text.encode('utf-8').decode('unicode-escape'))</code></pre>
<p><img src="http://rpddoc.weoa.com/server/../Public/Uploads/2019-08-19/5d5a606a40b31.jpg" alt="" /></p>