一、接口自动化介绍
1.简介
[接口自动化]是提高测试效率和质量的重要手段之一。
2.为什么要做接口自动化
相对于UI自动化而言,接口自动化具有更大的价值。
为了优化转化路径或者提升用户体验,APP/web界面的按钮控件和布局几乎每个版本都会发生一次变化,导致自动化的代码频繁变更,没有起到减少工作量的效果。
而接口一旦研发完成,后期重构/大幅度修改的频率则比较低.因而做接口自动化性价比还是很高的,对于迭代版本旧有功能的回归,beta测试,线上回归都能起到事半功倍的作用。
3.接口自动化两大类
1、为模拟测试数据而开展的接口自动化
这种接口自动化大多是单次执行,目的很明确是为了功能测试创造测试数据,节约人工造数据的时间和人工成本,提高功能测试人员的测试效率。
2、在功能测试之前提前发现错误而开展的接口自动化
这种接口自动化的工作流程跟功能测试一样,需要设计接口测试用例,然后执行接口测试用例。
说白了就是对单接口进行功能校验,包括接口参数的必填性、长度字符类型限制、入参枚举值等是否正确、响应数据是否正确等进行校验。
4.插件自动安装
1、在项目根目录创建文件:requirements.txt
2、在文件中写入需要安装的插件名
requirements.txt 内容
pytest
requests
pytest-html
pytest-xdist
pytest-ordering
pytest-rerunfailures
pytest-base-url
allure-pytest
二、Requests简介
1.Requests是基于urllib,使用Apache2 许可证开发HTTP库。其在python内置模块的基础上进行了高度封装,使得Requests能够轻松完成浏览器相关的任何操作。
2.接口测试流程
发起请求: 通过HTTP库向目标站点发起请求,等待目标站点服务器响应。
获取响应: 若服务器正常响应,会返回一个Response,该Response即为获取得页面内容,Response可以是HTML、JSON、字符串、二进制数据等数据类型。
解析内容:利用正则表达式、网页解析库对HTML进行解析;将json数据转为JSON对象进行解析;保存我们需要得二进制数据(图片、视频)。
内容断言:根据解析内容断言期望结果。
3.Requests总览
image.png
requests
requests请求 功能
requests.get( ) 从服务器获取数据
requests.post( ) 向服务器提交数据
requests.put( ) 从客户端向服务器传送的数据取代指定的文档的内容
requests.delete( ) 请求服务器删除指定页面
requests.head( ) 请求页面头部信息
requests.options( ) 获取服务器支持的HTTP请求方法
requests.patch( ) 向HTML提交局部修改请求,对应于HTTP的PATCH
requests.connect( ) 把请求连接转换到透明的TCP/IP通道
requests.trace( ) 回环测试请求,查看请求是否被修改
requests.session( ).get( ) 构造会话对象
requesets请求参数 含义
url 请求的网址
allow_redirects 设置是否重新定向
auth 设置HTTP身份验证
cert 指定证书文件或密钥的字符串
cookies 要发送至指定网址的Cookie字典
headers 要发送到指定网址的HTTP标头字典
proxies URL代理的协议字典
stream 指定响应后是否进行流式传输
timeout 设置等待客户端连接的时间
verify 用于验证服务器TLS证书布尔值或字符串指示
"""get请求"""
import requests
url = 'https://tse4-mm.cn.bing.net/th/id/OIP-C.w3cHPxIHKpLZodnlBoIZXgHaMx?w=182&h=314&c=7&o=5&dpr=1.45&pid=1.7'
response = requests.get(url)
print(res.status_code)
"""添加请求头:header"""
headers = {'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'}
response = requests.get('https://www.zhihu.com/explore',headers=headers)
print(response.status_code)
"""带请求参数"""
params = {'wd':'python'}
response = requests.get('https://www.baidu.com/',params=params)
print(response.status_code)
"""代理设置"""
proxies = {'http':'http://127.0.0.1:9743',
'https':'https://127.0.0.1:9742',}
response = requests.get('https://www.taobao.com',proxies=proxies)
print(rsponse.status_code)
"""SSL证书验证"""
response = requests.get('https://www.12306.cn',verify=False)
print(response.status_code)
"""超时设置"""
from requests.exceptions import ReadTimeout
try:
response = requests.get("http://httpbin.org/get", timeout = 0.5)
print(response.status_code)
except ReadTimeout:
print('timeout')
"""认证设置"""
from requests.auth import HTTPBasicAuth
response = requests.get("http://120.27.34.24:9001/",auth=HTTPBasicAuth("user","123"))
print(response.status_code)
"""post请求"""
import requests
import json
host = 'http://httpbin.org/'
endpoint = 'post'
url = ''.join([host,endpoint])
"""带数据的post"""
data = {'key1':'value1','key2':'value2'}
response = requests.post(url,data=data)
print(response.status_code)
print(response.text)
"""带headers的post"""
headers = {'User-Agent':'test request headers'}
response = requests.post(url,headers=headers)
print(response.status_code)
print(response.text)
"""带json的post"""
data = {
'sites':[
{'name':'test','url':'www.test.com'},
{'name':'google','url':'www.google.com'},
{'name':'weibo','url':'www.weibo.com'}
]
}
response = requests.post(url,json=data)
print(response.status_code)
print(response.text)
"""带参数的post"""
params = {'key1':'params1','key2':'params'}
response = requests.post(url,params=params)
print(response.status_code)
print(response.text)
"""文件上传"""
files = {'file':open('fisrtgetfile.txt','rb')}
response = requests.post(url,files=files)
print(response.status_code)
print(response.text)
"""put请求"""
import requests
import json
url = 'http://127.0.0.1:8080'
header = {'Content-Type':'application/json'}
param = {'myObjectField':'hello'}
payload = json.dumps(param)
response = requests.put(url,data=payload,headers=headers)
"""head请求"""
import requests
response = requests.head('https://pixabay.com/zh/')
print(response.status_code)
"""delete请求"""
import requests
url = 'https://api.github.com/user/emails'
email = '2475757652@qq.com'
response = requests.delete(url,json=email,auth=('username','password'))
print(response.status_code)
"""options请求"""
import requests
import json
url = 'https://www.baidu.com/s'
response = requests.options(url)
print(response.status_code)
response
response属性 功能
response.text 获取文本内容
response.content 获取二进制数据
response.status_code 获取状态码
response.headers 获取响应头
response.cookies 获取cookies信息
response.cookies.get_dict 以字典形式获取cookies信息
response.cookies.items 以列表形式获取cookies信息
response.url 获取请求的URL
response.historty 获取跳转前的URL
response.json 获取json数据
import requests
url = 'https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB/5162711'
response = requests.get(url)
print(response.status_code)
print(response.text)
print(response.json)
print(response.content)
print(response.headers)
print(response.cookies)
print(response.cookies.items)
print(response.url)
print(response.history)
print(response.cookies.get_dict)
"""下载图片"""
import requests
import matplotlib.pyplot as plt
url = 'https://cn.bing.com/images/search?view=detailV2&ccid=qr8JYj0b&id=6CEE679B0BCE19C94FB9C7595986720942C92261&thid=OIP.qr8JYj0bcms3xayruiZmnAHaJQ&mediaurl=https%3a%2f%2ftse1-mm.cn.bing.net%2fth%2fid%2fR-C.aabf09623d1b726b37c5acabba26669c%3frik%3dYSLJQglyhllZxw%26riu%3dhttp%253a%252f%252fp1.ifengimg.com%252f2019_02%252f95A41E54C3C8EB3B3B148A30CE716314B0AED504_w1024_h1280.jpg%26ehk%3d2EhKcVkSnCvT6uBfgisn%252fdwtghMXFWjGa5WgqEbBSPc%253d%26risl%3d%26pid%3dImgRaw&exph=1280&expw=1024&q=%e7%9f%b3%e5%8e%9f%e9%87%8c%e7%be%8e&simid=607996751665040666&FORM=IRPRST&ck=399D74E04F8507D6711ADC8F53A714D7&selectedIndex=0&ajaxhist=0&ajaxserp=0'
response = requests.get(url)
img = response.content
with open('shiyuanlimei.jpg','wb') as f:
f.write(img)
三、Yaml知识点
1.Yaml简介
YAML 是一种可读性非常高,与程序语言数据结构非常接近。同时具备丰富的表达能力和可扩展性,并且易于使用的数据标记语言。
其实YAML文件也是一种配置文件,但是相较于ini,conf配置文件来说,更加的简洁,操作简单,还能存放不同类型的数据;而像ini存储的值就都是字符串类型,读取之后还要手动转换
2.YAML的基本语法规则
大小写敏感
使用缩进表示层级关系
缩进时不允许使用Tab键,只允许使用空格。(可以将你的ide的tab按键输出替换成4个空格)
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
表示注释
3.YAML支持的数据结构
对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
纯量(scalars):单个的、不可再分的值
4.YAML,对象数据类型
对象的一组键值对,使用冒号结构表示。
animal: dogs
转换成Python数据结构,如下:
{'animal': 'dogs'}
将所有键值对赋值。
hash: { name: Steve, foo: bar }
转换成Python数据结构,如下:
{'hash': {'name': 'Steve', 'foo': 'bar'}}
将列表赋值
lists : [1,2,3]
转换成Python数据结构,如下:
{'lists': [1, 2, 3]}
将元组赋值
tuples : (1,2,3)
转换成Python数据结构,如下:
{'tuples': '(1,2,3)'}
总结
当赋值列表、键值对时,转换成Python数据结构是可以直接当列表、字典使用的;
当赋值元组时,转换后也是字符串
最终输出的都是字典类型,可以通过key获取对应的值
以 - 开头的行表示构成一个数组
四、python+requests+pytest+yaml+allure接口自动化框架
一.common层
logger_util.py文件
1.使用了python中的loguru库来进行日志记录,从loguru库中引入logger对象,使用logger.add()方法配置日志记录器.
2.把日志写入到名为logs_{time:YYYY-MM-DD-HH-mm-ss.sss}.log的文件中
3.{time:YYYY-MM-DD-HH-mm-ss.sss}将在日志文件名中被替换为当前时间.
4.rotation=10MB是日志文件当大小达到10MB时自动轮转
5.compression=zip是会使用zip进行压缩
6.定义了info(),debug(),warning(),error(),critical()函数,分别用来记录信息,调试,警告,错误,严重错误级别的日子,使用logger对象的相应方法将信息记录到日志文件中
request_util.py文件
这段代码定义了一个名为RequestUntil的类,并包含一个名为send_request的方法。类中定义了一个静态变量session来保存持久化的session对象。
send_request方法包装了requests库的request方法,它接收三个参数:http请求方法(例如:GET,POST,PUT等),url和关键字参数。(关键字参数是根据请求不同而不同的各种参数)
首先,该方法使用loguru库中的info()方法来记录发送请求的参数。然后,使用requests库的request方法来发送HTTP请求,返回响应对象并将其保存在res变量中。
接下来,使用loguru库中的debug()方法来记录响应对象的状态码和内容。最后返回响应对象。
Yaml_util.py文件
这段代码是一个关于读写YAML文件的工具方法集合。首先,它从Python的os库中导入了getcwd和path的方法,同时它从yaml库中导入了load和dump方法。
1.get_object_path方法使用os库返回当前工作目录路径。如果当前工作目录中包含字符串“common”,它将返回“common”之前的所有内容作为项目的根目录目录。
2.read_yaml方法使用yaml库的load()方法来读取yaml文件(extract.yaml)的内容,并返回包含yaml内容的Python字典。
3.write_yaml方法使用yaml库的dump()方法将数据写入到yaml文件(extract.yaml)中。流模式被传递到yaml dump()方法中,以确保数据可以整个传输。
4.clear_yaml方法清空yaml文件(extract.yaml)中的内容,它在打开文件后将文件截断为0。
5.read_testcase方法接收一个路径参数,然后从指定路径读取yaml文件的内容,返回包含yaml内容的Python字典。这个方法主要用于读取测试用例文件
二.data层
test_get_token.yaml文件
这是一个YAML格式的测试用例文件,测试用例名为“获取接口统一鉴权码token接口”。以下是该测试用例的详细信息:
- name: 测试用例名称,即“获取接口统一鉴权码token接口”
- request: 请求信息
- method: 请求方法,即“get”
- url: 请求URL,即“https://api.weixin.qq.com/cgi-bin/token”
- params: GET请求的查询字符串参数,包括两个键值对:
- grant_type: “client_credential”
- appid: “**********”
三.testcase层
test_API.py文件
执行接口请求区域
四.框架的介绍:
1.common层构建基础类库:为了提高测试用例的可维护性性和复用性,其中包括日志输出,接口统一发送请求,yaml文件的读取,
写入,清除的方法
2.data层:存放测试用例yaml数据
3.logs层:存放日志
4.reports层:存放生成的allure报告
5.temps层:存放生成的临时报告
6.testcase层:测试用例
7.pytest.ini:配置文件
8.requirements.txt:依赖包
9.run.py:自动化框架运行脚本的文件,然后会在reports生成allure报告
框架的使用说明:
1.环境配置:需要在pycharm终端里安装requirements.txt依赖包,输入pip install -requirements.txt部署环境
2.编写测试用例数据是用yaml文件进行传参,其中包含测试所有API的参数,这个文件可以包括
接口名称(name),请求(request),请求方法(menthod),请求路径(URL),请求参数(params)
举例:
- name: 获取接口统一鉴权码token接口
request:
method: get
url: https://api.weixin.qq.com/cgi-bin/token
params:
grant_type: client_credential
appid: XXXXXXXXXXXX
secret: XXXXXXXXXXXXX
3.测试用例以test_XXX命名,XXX是测试用例的名字,其中包括allure标记的装饰器,数据驱动,断言,
@allure.story(),allure.dynamic.title()是allure标记的装饰器
@pytest.mark.parametrize("caseinfo", read_testcase("./data/test_get_token.yaml"))
这行代码使用了pytest框架提供的 @pytest.mark.parametrize()装饰器,具体含义是read_testcase()yaml文件
读取的函数从路径"./data/test_get_token.yaml"文件中读取到测试用例数据集作为参数,将其命名为caseinfo,并
传递给被装饰器的测试函数.
test_get_token()的测试函数,该函数使用了参数化方式运行多组测试用例,每组测试用例都是一个字典函数,包含了请求参数
和预期结果信息,用yaml数据格式进行value:key方式进行传参.
断言用是pytest自带的断言方式assert
举例:
@pytest.mark.parametrize("caseinfo", read_testcase("./data/test_get_token.yaml"))
@allure.story("获取token接口")
def test_get_token(self, caseinfo):
allure.dynamic.title("获取token")
method = caseinfo["request"]["method"]
url = caseinfo["request"]["url"]
params = caseinfo["request"]["params"]
res = RequestUntil().send_request(method=method, url=url, params=params)
print(res.json()["access_token"])
write_yaml(res.json()["access_token"])
assert res.status_code== 200
assert "access_token" in res.text
4.执行测试
点击run.py文件.执行脚本,然后会在reports包里生成一份allure报告,点击idex.html,点击浏览器就可以查看
本地allure报告
网友评论