Python-Pytest-Requests-API接口测试自动化框架2

一、

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()  # 清空
相关推荐
程序之巅4 小时前
VS code 远程python代码debug
android·java·python
__如风__5 小时前
onlyoffice文档转换服务离线部署
python
今晚务必早点睡5 小时前
写一个Python接口:发送支付成功短信
开发语言·python
ada7_5 小时前
LeetCode(python)22.括号生成
开发语言·数据结构·python·算法·leetcode·职场和发展
2501_941871455 小时前
面向微服务链路追踪与全局上下文管理的互联网系统可观测性设计与多语言工程实践分享
大数据·数据库·python
luoluoal5 小时前
基于python的语音和背景音乐分离算法及系统(源码+文档)
python·mysql·django·毕业设计·源码
love530love5 小时前
EPGF 新手教程 12在 PyCharm(中文版 GUI)中创建 Poetry 项目环境,并把 Poetry 做成“项目自包含”(工具本地化为必做环节)
开发语言·ide·人工智能·windows·python·pycharm·epgf
cute_ming5 小时前
从 Node.js + TypeScript 无缝切换到 Python 的最佳实践
python·typescript·node.js
2501_941870565 小时前
从配置频繁变动到动态配置体系落地的互联网系统工程实践随笔与多语言语法思考
java·前端·python
西西弗Sisyphus6 小时前
Python FastAPI 和 Uvicorn 同步 (Synchronous) vs 异步 (Asynchronous)
python·fastapi·uvicorn