接口自动化---进阶
让程序代替人工生产测试数据并判断响应的结果是否符合预期。作用:接口升级(迭代)时,会使用接口自动化测试实现:只测试主要的或被重复使用的接口(功能测试需要所有接口)只需要设计正向数据自动化测试可以被重复执行,多次执行自动化测试时尽量不要使用关联,可以测试某个指定接口自动化测试是对功能测试的补充,接口升级时,可以借助于自动化测试,检查升级前的接口实现是否能够正常运行。1.它是面向http协议的测试框架
导图预览
一、什么是接口自动化
让程序代替人工生产测试数据并判断响应的结果是否符合预期。
作用:接口升级(迭代)时,会使用接口自动化测试
原则:
- 只测试主要的或被重复使用的接口(功能测试需要所有接口)
- 只需要设计正向数据
- 一般建议用最少的用例覆盖最多的业务场景,比如:登录-搜索商品-加入购物车-下单-支付
- 自动化测试可以被重复执行,多次执行
- 自动化测试时尽量不要使用关联,可以测试某个指定接口
总结:
自动化测试是对功能测试的补充,接口升级时,可以借助于自动化测试,检查升级前的接口实现是否能够正常运行。
二、为什么要做接口自动化?
1.接口数量大,敏捷开发,团队实现接口测试,版本控制
2.有些接口工具无法实现(加密的复杂接口,签名接口等)
3.处理不同协议的接口
4.提供更简洁明了的报告
5.多接口串联,数据库验证,日志监控
6.web自动化+接口自动化同时做的时候。
三、python+requests模块
requests第三方库,主要用于发送http请求,做接口自动化。
get请求通过params传递参数;
post请求通过json或者data传递参数。
json和data的区别:
json:
数据报文:不管是dict还是str类型,默认都是application/json,格式:{“a”:1,"b":2}
一般只能传dict格式的。
data:
数据报文:dict字典类型,那么默认情况下请求头:application/x-www-form-urlencoded; 以form表单的方式传参,格式:a=1&b=2
数据报文:str类型,那么默认情况下是:text/plain
若在其他情况下,没法直接传参,那么可以将对应的参数转换为属性支持的参数格式:
- json.dumps(data) 序列化:将字典格式的数据转换成str格式
- json.loads(data) 反序列化:把str格式转换成字典格式
通过requests做接口自动化
import requests
from pprint import pprint
res=requests.get('http://localhost/mgr/ps/mgr/index.html#/')
if res.status_code==200:
print('测试通过')
else:
print('接口存在问题')
print(res.json()) #将结果以json形式展示
import requests
from pprint import pprint
res=requests.post('http://localhost/apijson/mgr/sq_mgr/',
data={
"action" : "add_course",
"data" : '''{
"name":"初中121323化学",
"desc":"初中121323化学课程",
"display_idx":"4"
}
'''
})
retobj=res.json()
if retobj['retcode']==0:
print('测试通过')
else:
print('接口存在问题')
pprint(res.json(),width=30)
带请求头和cookies的接口需要cookie鉴权,有两种方式:
1.网页的接口基本上都要做cookie鉴权
2.通过session实现cookie鉴权----常用方式
封装请求
接口自动化框架封装的第一步,统一请求方式。
requests.request()
例:
req=requests.request("post",url=url,data=data,headers=headers)
案例:
框架封装:统一请求和通过session进行cookie鉴权
class Test_phpwind:
cookies=""
csrf_token=""
#设置一次会话session,相当于保存一个身份鉴权,用于整个会话访问过程
session=requests.session()
def test_firstPage():
url='http://47.107.116.139/phpwind/'
req=Test_phpwind.session.request("get",url=url)
# print(req.text)
reg='name="csrf_token" value="(.*?)"'
Test_phpwind.csrf_token=re.search(reg,req.text)[1]
print(Test_phpwind.csrf_token)
Test_phpwind.cookies=req.cookies
print(Test_phpwind.cookies)
def test_login():
url="http://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"
data={
"username":"ashley",
"password":"zxcvbnm...",
"backurl":"http://47.107.116.139/phpwind/",
"invite":"",
"csrf_token":Test_phpwind.csrf_token
}
headers={
"Accept": "application/json, text/javascript, */*; q=0.01",
"X-Requested-With": "XMLHttpRequest"
}
print("+"*10)
print(Test_phpwind.csrf_token)
print(Test_phpwind.cookies)
req=Test_phpwind.session.request("post",url=url,data=data,headers=headers)
print("----------------")
print(req.json())
if __name__=="__main__":
# pytest.main(['-vs'])
Test_phpwind.test_firstPage()
Test_phpwind.test_login()
接口关联的封装
通过一个关联的yaml文件进行封装。
实现:将第一步的token取出写入yaml文件。然后需要用到token的地方从yaml中读取出即可。
主要case代码:
test.py
import re
import requests
from readYaml import readYaml
class Test_phpwind:
session=requests.session()
def test_firstPage():
url='http://47.107.116.139/phpwind/'
req=Test_phpwind.session.request("get",url=url)
# print(req.text)
reg='name="csrf_token" value="(.*?)"'
csrf_token=re.search(reg,req.text)[1]
data={"csrf_token":csrf_token}
# 为什么穿不了参数,提示没有传data,实际上有了呀?
# readYaml.readYaml.write_contract_yaml(data)
readYaml().write_contract_yaml(data)
def test_login():
# 整个一直报错是因为从yaml中获取到的是一个dict类型,需要获取token值即可
# csrf_token=readYaml().read_contract_yaml("csrf_token")
csrf_token=readYaml().read_contract_yaml("csrf_token")['csrf_token']
print("="*10)
print(csrf_token)
url="http://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"
data={
"username":"ashley",
"password":"zxcvbnm...",
"backurl":"http://47.107.116.139/phpwind/",
"invite":"",
"csrf_token":csrf_token
}
headers={
"Accept": "application/json, text/javascript, */*; q=0.01",
"X-Requested-With": "XMLHttpRequest"
}
print("+"*10)
req=Test_phpwind.session.request("post",url=url,data=data,headers=headers)
print("----------------")
print(req.json())
if __name__=="__main__":
# pytest.main(['-vs','--reruns=3'])
Test_phpwind.test_firstPage()
Test_phpwind.test_login()
yaml文件的读写:
readYaml.py
import os
import yaml
class readYaml:
#读取yaml文件
def read_contract_yaml(self,params):
with open(os.getcwd()+'/contract.yaml',mode='r',encoding="utf-8") as f:
value=yaml.load(stream=f,Loader=yaml.FullLoader)
return value;
#写入yaml文件内容
def write_contract_yaml(self,data):
with open(os.getcwd()+"/contract.yaml",mode='w',encoding='utf-8') as f:
yaml.dump(data=data,stream=f,allow_unicode=True)
def clear_yaml():
with open(os.getcwd()+"/contract.yaml",mode='r',encoding='utf-8') as f:
f.truncate()
print("清除yaml内容")
conftest文件
主要是用来写pytest.fixture()方法的。
详细介绍见:https://editor.csdn.net/md/?articleId=126467461
conftest.py
import pytest
import readYaml
@pytest.fixture(scope="function")
def conn_database():
print('连接数据库')
yield
print("关闭数据库")
def clear_yaml():
readYaml.readYaml.clear_yaml()
断言
通过断言来判断接口是否通
def test_login(self):
# 整个一直报错是因为从yaml中获取到的是一个dict类型,需要获取token值即可
# csrf_token=readYaml().read_contract_yaml("csrf_token")
csrf_token=readYaml().read_contract_yaml("csrf_token")['csrf_token']
print("="*10)
print(csrf_token)
url="http://47.107.116.139/phpwind/index.php?m=u&c=login&a=dorun"
data={
"username":"ashley",
"password":"zxcvbnm...",
"backurl":"http://47.107.116.139/phpwind/",
"invite":"",
"csrf_token":csrf_token
}
headers={
"Accept": "application/json, text/javascript, */*; q=0.01",
"X-Requested-With": "XMLHttpRequest"
}
req=Test_phpwind.session.request("post",url=url,data=data,headers=headers)
print(req.json())
result=req.json()
assert result["state"] in "success"
assert 'refresh' in result
yaml文件
yaml主要用于配置文件,或者用例文件或者保存接口关联的值。主要的数据有以下两种:
- map对象:键值对:name:value
- 列表类型:用 - 开头的
- name:xxx
- request:xxx
- url:xxx
yaml文件的规则
1.必须包含一级关键字:name,request,validate
2.在request关键字下必须包括method、url、data,如果没有data的话,那么输出默认值。
3.提取变量使用以及关键字:extract 。支持json提取和正则提取(.+?)或(.*?),取值使用{{ }}
4.可以使用热加载的方式调用debug_talk.py中的DebugTalk类里面的方法。通过${ }调用方法
5.支持equals,contains两种断言
6.使用parameters做csv文件的数据驱动,通过$csv{appid}这种格式取值
例:
parameters:
name-appid-secret-grant_type-assert_str:data/get_token_data.csv
-
name:获取鉴权码token
request:
method:get
url:
data:
grant_type:xxx
validate:
- equals:{status_code:200}
- contains:asscess_token
语法规则:
1、区分大小写
2、支持注释,用#注释
3、使用缩进判断层级关系
对接口参数的判断
使用DDT实现自动化
DDT(data -driven tests)数据驱动框架,指的是在自动化测试中处理测试数据的方式。
一般是测试数据与功能函数分离,存储在功能函数的外部位置。在自动化测试运行时,数据驱动框架会读取数据源中的数据,把数据作为参数传递到功能函数中,然后根据数据的条数多次运行同一个功能函数。
数据驱动的数据源可以时csv、excel、txt、数据集合以及数据库等形式。
优点:
- 能够减少代码重复
- 一条用例失败,不会影响其他测试数据对应的用例
DDT的使用步骤:
1、使用@ddt装饰你的测试类
2、使用@data或@file_data装饰需要数据驱动的测试方法
3、如果一组数据中有多个参数,可以通过@unpack装饰测试方法来达到解包效果,然后通过变量来接收
代码实现:
from ddt import ddt,data
@ddt #ddt一定是装饰在TestCase的子类上
class Test_phpwind(unittest.TestCase):
session=requests.session()
host='http://120.55.190.222:9090'
#测试登录接口
#获取excel数据,*号代表返回的是可变参数
@data(*ExcelUtils().get_excelData())
@unpack #对每一组数据,如果是list或tuple形式,将他们分拆成独立的参数
def test_01_login(self,index,username,pwd,ranCode):
print(index,username,pwd,ranCode)
url1=self.host+"/loginController.do?login2"
url=self.host+"/loginController.do?checkuser"
data={
"returnUrl": None,
"userName":username,
"password":pwd,
"randCode":ranCode
}
headers={
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"X-Requested-With": "XMLHttpRequest"
}
req1=self.session.request("post",url=url1,data=data,headers=headers)
req2=self.session.request("post",url=url,data=data,headers=headers)
print( req2.content.decode("utf-8"))
result=json.loads(req2.content.decode("utf-8"))
print(result['msg'])
if index==1:
assert '操作成功' in result['msg']
print(result['msg'])
else:
assert bool("false") in result['success']
print(result['msg'])
注意:data中的参数函数返回值一定需要遵守ddt.data()可接受的数据格式,即:一组数据,每个数据为单个的值;多组数据,每组数据为一个列表或一个字典。
如果是通过传入文件(json或yaml文件)提取数据,则通过@filedata()来装饰方法。@filedata(‘test_data.json’)
拓展见:https://www.jianshu.com/p/e5dcdf4d9e77
接口自动化需要了解的知识:
httprunner知识见:https://editor.csdn.net/md?not_checkout=1&spm=1011.2124.3001.6192&articleId=134847369
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)