一、
1.基于工具类的:
(1)postman+newman+jenkins
(2)jmeter+ant+jenkins
(3)apifox+apifox-cli+jenkins
2.基于框架和平台:
python+requests+pytest+allure+yaml+logging+jenkins
3.开发自动化测试平台
二、常见请求--Request请求部分
1.get
(1)def get(url,params=None, **kwargs)
url:接口地址
params:参数,在get请求的url后面传的参数
**kwargs:可变长度的字典
(2)**kwargs 是 "关键字参数(keyword arguments)" 的缩写,作用是接收函数调用时传入的「多余的关键字参数」,并把它们打包成一个字典。
python
import jsonpath
import requests
# **kwargs 是 "关键字参数(keyword arguments)" 的缩写,
# 作用是接收函数调用时传入的「多余的关键字参数」,
# 并把它们打包成一个字典。
class TestApi:
# 获取access_token鉴权码接口
def test_get_token(self):
urls="https://api.weixin.qq.com/cgi-bin/token"
datas={
"grant_type":"client_credential",
"appid":"wx8a9de038e93f77ab",
"secret":"8326fc915928dee3165720c910effb86"
}
res=requests.get(url=urls, params=datas)
# list=jsonpath.jsonpath(res.json(),'$.access_token') # 接口关联方式一:jsonpath提取实现接口关联
# print(list[0])
token=res.json()['access_token']
print(token)
print(res.json())
# 查询标签接口
# def test_select_flag(self):
# urls = "https://api.weixin.qq.com/cgi-bin/tags/get"
# datas={
# "access_token": "???"
# }
# res=requests.get(url=urls, params=datas)
# print(res.json())
if __name__ == '__main__':
# TestApi().test_select_flag()
test_obj = TestApi()
# 1. 先执行test_get_token,获取并保存access_token
test_obj.test_get_token()
# 2. 再执行test_select_flag,这时就能拿到access_token了
# test_obj.test_select_flag()
2.def post(url,data=None,json=None,**kwargs):0
url:接口地址>
data:参数(表单/表格---x-www-form-urlencoded)
json:参数(JSON---application/json) --请求头
**kwargs:可变长度的字典
3.def put(url, data=None,**kwargs):
4.def delete(url,**kwargs):
5.四种方法底层都调用的是request方法
(1)requests.request("get",url,params=params,**kwargs)
(2)requests.request()调用的是session.request方法 session.request(method=method, url=url,**kwargs, ---最底层方法 ---接口自动化所用
(3)requests.request与session.request的区别?
requests.request的每个请求是独立的,而session.request会自动关联所有请求的cookie信息
5.Postman四种传参方式:
(1)form-data:既有表单,又有文件上传
requests中对应files
(2)x-www-form-urlencoded: 纯表单
requests中对应data
(3)raw:传json格式的参数 json:application/json
requests中对应json
(4)binary:传二进制文件---转换为二进制再上传 application/octrent-stream
requests中对应data
三、Request响应部分
1.常见的返回信息
(1)res .text
(2)res.json()返回字典形式的结果
(3)res.content 返回字节类型的结果
(4)res.status_code返回状态码
(5)res.reason 返回状态信息
(6)res.cookie 返回cookie信息
(7)res.encoding 返回编码格式
(8)res.headers 返回响应头
(9)res.request.xxx 得到请求数据
2.接口关联的三种层次:
(1)通过类变量保存中间变量实现接口关联 --可能会有不同用例(.py文件)的相互调用,而形成递归
python
"""
通过类变量,保存中间变量,实现接口关联
"""
import requests
class TestApi:
# 接口关联的三种层次:(1)通过类变量保存中间变量实现接口关联
access_token="" # 类变量,是一个全局变量,可以在下面的用例(test_select_flag)去执行
# 获取access_token鉴权码接口
def test_get_token(self):
urls="https://api.weixin.qq.com/cgi-bin/token"
datas={
"grant_type":"client_credential",
"appid":"wx8a9de038e93f77ab",
"secret":"8326fc915928dee3165720c910effb86"
}
res=requests.get(url=urls, params=datas) # get请求,通过params传参
TestApi.access_token=res.json()['access_token']
print(TestApi.access_token)
# 查询标签接口
def test_select_flag(self):
urls = "https://api.weixin.qq.com/cgi-bin/tags/get"
datas={
"access_token": TestApi.access_token
}
res=requests.get(url=urls, params=datas)
print(res.json())
# 编辑标签接口,--post 传递json格式数据
def test_edit_flag(self):
urls = "https://api.weixin.qq.com/cgi-bin/tags/update" # url跟在路径后,用params传 --实际是**kwargs里的params
datas1={
"access_token": TestApi.access_token
}
datas2={
"tag":{"id":134,"name":"广东人"}
}
res=requests.post(url=urls, json=datas2, params=datas1)
print(res.json())
# 或者让token跟在后面,也可以成功传递
# def test_edit_flag(self):
# urls = "https://api.weixin.qq.com/cgi-bin/tags/update?access_token="+TestApi.access_token # url跟在路径后,用params传 --实际是**kwargs里的params
#
# datas2={
# "tag":{"id":134,"name":"广东人"}
# }
# res=requests.post(url=urls, json=datas2)
# print(res.json())
# 文件上传-files 参数类型 data
def test_file_upload(self):
urls = "https://api.weixin.qq.com/cgi-bin/media/uploadimg" # url跟在路径后,用params传 --实际是**kwargs里的params
datas1={
"access_token": TestApi.access_token
}
datas2={
"media":open(r"D:\相册\图片\2.jpg","rb") # rb 只读 二进制
}
res=requests.post(url=urls, files=datas2, params=datas1)
print(res.json())
if __name__ == '__main__':
test_obj = TestApi()
test_obj.test_get_token()
# 2. 再执行test_select_flag,这时就能拿到access_token了
test_obj.test_select_flag()
# 3.编辑
test_obj.test_edit_flag() # 这里要求错误码为0;即错误码为0代表测试通过
#4.文件上传
test_obj.test_file_upload()
python
"""
通过类变量,保存中间变量,实现接口关联
"""
import requests
from commons.requests_util import RequestsUtil
class TestApi:
# 接口关联的三种层次:(1)通过类变量保存中间变量实现接口关联
access_token="" # 类变量,是一个全局变量,可以在下面的用例(test_select_flag)去执行
# 获取access_token鉴权码接口
def test_get_token(self):
urls="https://api.weixin.qq.com/cgi-bin/token"
datas={
"grant_type":"client_credential",
"appid":"wx8a9de038e93f77ab",
"secret":"8326fc915928dee3165720c910effb86"
}
res=RequestsUtil().send_all_request(method="get",url=urls,params=datas) # get请求,通过params传参
TestApi.access_token=res.json()['access_token']
print(TestApi.access_token)
# 查询标签接口
def test_select_flag(self):
urls = "https://api.weixin.qq.com/cgi-bin/tags/get"
datas={
"access_token": TestApi.access_token
}
res=RequestsUtil().send_all_request(method="get",url=urls, params=datas)
print(res.json())
# 编辑标签接口,--post 传递json格式数据
def test_edit_flag(self):
urls = "https://api.weixin.qq.com/cgi-bin/tags/update" # url跟在路径后,用params传 --实际是**kwargs里的params
datas1={
"access_token": TestApi.access_token
}
datas2={
"tag":{"id":134,"name":"广东人"}
}
res=RequestsUtil().send_all_request(method="post",url=urls, json=datas2, params=datas1)
print(res.json())
# 或者让token跟在后面,也可以成功传递
# def test_edit_flag(self):
# urls = "https://api.weixin.qq.com/cgi-bin/tags/update?access_token="+TestApi.access_token # url跟在路径后,用params传 --实际是**kwargs里的params
#
# datas2={
# "tag":{"id":134,"name":"广东人"}
# }
# res=requests.post(url=urls, json=datas2)
# print(res.json())
# 文件上传-files 参数类型 data
def test_file_upload(self):
urls = "https://api.weixin.qq.com/cgi-bin/media/uploadimg" # url跟在路径后,用params传 --实际是**kwargs里的params
datas1={
"access_token": TestApi.access_token
}
datas2={
"media":open(r"D:\相册\图片\2.jpg","rb") # rb 只读 二进制
}
res=RequestsUtil().send_all_request(method="post",url=urls, files=datas2, params=datas1)
print(res.json())
if __name__ == '__main__':
test_obj = TestApi()
test_obj.test_get_token()
# 2. 再执行test_select_flag,这时就能拿到access_token了
test_obj.test_select_flag()
# 3.编辑
test_obj.test_edit_flag() # 这里要求错误码为0;即错误码为0代表测试通过
#4.文件上传
test_obj.test_file_upload()
(2)通过单独的文件保存中间变量实现接口关联。 ---封装
(3)极限封装成零代码的方式实现接口关联。
3.接口关联两种方式:
(1)正则提取实现接口关联
re.seach()通过正则匹配一个值,通过下标[1]取值,没有匹配到返回None
re.findall()通过正则匹配多个值,返回List,通过下标取值,没有匹配到返回None
(2)JsonPath提取实现接口关联
jsonpath.jsonpath()返回一个列表,通过下标取值,没有找到返回None
4.re.search()
(1)re.search()的 .group(1) 是用来提取正则表达式中第一个括号分组匹配到的内容,是Python正则模块中获取分组结果的核心用法。
(2)re.search(pattern, string) :在字符串中搜索符合正则表达式 pattern 的内容,返回一个 Match 对象(匹配到结果时)或 None (未匹配到)。
(3) 括号 () 在正则中的作用:表示分组,会把括号内匹配到的内容单独保存,方便后续提取。
(4)group(1) 的含义:
group(0) (或直接 group() ):返回整个正则表达式匹配到的完整内容。
group(1) :返回第一个括号分组匹配到的内容。
若有多个分组(如 (a)(b) ),则 group(2) 返回第二个分组内容,以此类推。
如:TestApi3.csrf_token=re.search('name="csrf_token" value="(.*?)"',res.text).group(1)
表达式里只有一组(),也就是(.*?),这就是第一个括号分组
(5)如果正则写成: (name)="csrf_token" (value)="(.*?)" ,就有三个括号分组:
第一个分组: (name) → group(1) 提取 name
第二个分组: (value) → group(2) 提取 value
第三个分组: (.*?) → group(3) 提取 csrf_token 值
四、接口自动化测试框架的封装(统一请求的封装)
1.进行统一请求封装的原因
(1)去除重复冗余的代码
(2)实现统一的异常处理以及日志监控
2.先封装,再写用例
五、用例管理框架pytest
1.作用:
(1)发现用例:默认发现用例的规则
模块名必须|以test 开头或者 test结尾。
测试类必须以Test开头。
测试方法必须以test 开头
(2)执行用例
(3)判断结果
(4)生成报告
(1)可以结合selenium,requests,appium,实现web,接口,app自动化
(2)可以结合Allure生成美观的报告,可以结合jenkins实现持续集成
(3)丰富的插件
3.插件
(1)pytest
(2)pytest-html 生成pytest报告
(3)pytest-xdist 多线程执行
(4)pytest-ordering 控制用例的执行顺序
(5)pytest-returnfailures 失败用例重跑
(6)pytest-base-url 基础路径(测试环境、开发环境、生产环境)
(7)allure-pytest 生成allure报告
把这些放在requirements.txt文件里,一次性安装
pip install -r requirements.txt
python
"""
通过类变量,保存中间变量,实现接口关联
"""
import pytest
from commons.requests_util import RequestsUtil
# fixture固件 False :不自动执行;想让哪个函数使用,将connection_mysql作为参数传入
@pytest.fixture(scope='function',autouse=False,params=["mysql","redis"]) # 指定作用范围 --函数
def connection_mysql(request): # 加参数request 可以把参数 params[]里的内容打印
print("连接数据库...") # 每个函数之前执行
# 通过yield 唤醒后置
yield request.param # 两个参数 mysql redis 用例会执行两次
print("关闭数据库连接")
class TestApi:
# 接口关联的三种层次:(1)通过类变量保存中间变量实现接口关联
access_token="" # 类变量,是一个全局变量,可以在下面的用例(test_select_flag)去执行
# # 在用例之前、之后进行某些操作
# def setup_method(self):
# print("-----每个用例之前的操作-----")
#
# def teardown_method(self):
# print("-----每个用例之后的操作-----")
#
# # 每个类之前、之后的操作
# def setup_class(self):
# print("*****每个类之前的操作*****")
#
# def teardown_class(self):
# print("*****每个类之后的操作*****")
# 获取access_token鉴权码接口
# 标记为冒烟测试
@pytest.mark.smoke
def test_get_token(self,base_url,connection_mysql):
print(connection_mysql)
urls=base_url+"/cgi-bin/token"
datas={
"grant_type":"client_credential",
"appid":"wx8a9de038e93f77ab",
"secret":"8326fc915928dee3165720c910effb86"
}
res=RequestsUtil().send_all_request(method="get",url=urls,params=datas) # get请求,通过params传参
TestApi.access_token=res.json()['access_token']
print(TestApi.access_token)
# 查询标签接口
# 标记为 用户管理
@pytest.mark.user
def test_select_flag(self):
urls = "https://api.weixin.qq.com/cgi-bin/tags/get"
datas={
"access_token": TestApi.access_token
}
res=RequestsUtil().send_all_request(method="get",url=urls, params=datas)
print(res.json())
# 编辑标签接口,--post 传递json格式数据
def test_edit_flag(self):
urls = "https://api.weixin.qq.com/cgi-bin/tags/update" # url跟在路径后,用params传 --实际是**kwargs里的params
datas1={
"access_token": TestApi.access_token
}
datas2={
"tag":{"id":134,"name":"广东人"}
}
res=RequestsUtil().send_all_request(method="post",url=urls, json=datas2, params=datas1)
print(res.json())
4.Pytest用例管理框架执行方式---三种
(1)命令行
(2)主函数
(3)通过配置文件(pytest.ini)来改变以及执行用例
(4)命令行方式、主函数方式,都会读取pytest.ini配置文件
六、Pytest用例管理框架的前后置(固件/夹具)
(1)可以在 用例之前、用例之后,做一些操作 setup teardown 使用少
(2)一般使用 fixture 固件
(3)fixture是一个装饰器,可装饰函数和类
@pytest.fixture(scope="作用域",params="参数化",autouse="是否自动执行",ids="参数别名",name="装饰器/夹具别名")
2.fixture参数解释:
(1)scope:funciton class moudle seesion(整个会话作用于)
(2)autouse : true 和 false
(3)params : 参数化
params=["mysql","redis"] 参数有几个,用例就执行几次
需要使用request参数 和yield
request.params
3.fixture 夹具,就是在用例(类 模块 会话)之前和之后做一些操作
三、接口关联封装(基于一个独立的YAML文件)
1.yaml_util类
python
# yaml 工具类
import os
import yaml
# 1.写入(追加写入 a+)
def write_yaml(data):
with open(os.getcwd()+"/extract.yaml", encoding="utf-8",mode="a+") as f:
# stream=f 输出流 f 是open的文件
# allow_unicode=True 允许yaml输出中保留Unicode字符(如中文)避免转义导致乱码
yaml.dump(data,stream=f,allow_unicode=True) # 写入
# 2.读取
def read_yaml(key):
with open(os.getcwd()+"/extract.yaml", encoding="utf-8",mode="r") as f:
# FullLoader 安全加载器 解析f 中的yaml内容,拒绝执行任意Python代码
value=yaml.load(f,yaml.FullLoader)
return value[key]
# 3.清空
def clean_yaml():
with open(os.getcwd()+"/extract.yaml", encoding="utf-8",mode="w") as f:
f.truncate() # 清空