python+requests+pytest 接口自动化实现

最近工作之余拿公司的项目写了一个接口测试框架,功能还不是很完善,算是抛砖引玉了,欢迎各位来吐槽。
主要思路:
①对 requests 进行二次封装,做到定制化效果
②使用 excel 存放接口请求数据,作为数据驱动
③里面有一些功能模仿了 jmeter,比如用户参数定义、jsonpath 提取
④用 pytest 进行测试用例管理

一、环境

python==3.8.0
requests==2.31.0
pytest==7.4
还有一些其他第三方库例如 allure 报告、jsonpath 等
这块不过多介绍,安装 python 配置环境变量,pip install 命令安装所需插件即可

二、项目目录结构

1、config:用来存

放项目配置文件
包括接口基础配置,数据库信息等
使用的是 configparser 进行读取

2、Outputs 层存放了日志和测试报告,这里报告用的是 allure

3、resources 存放的是测试数据 (这里用的 excel 作为接口数据驱动) 以及其他的一些测试文件等

4、Testcases 存放测试类

5、utils:工具类,包含了读取配置、记录日志、http 请求、接口数据处理等功能

三、重点功能介绍

1、读取 excel

实现代码如下:

from openpyxl import load_workbookclass DoExcel:def __init__(self, file_name):  # 打开文件self.filepath = os.path.join(resources,'case_datas', f'{file_name}.xlsx')# 获取工作表self.wb = load_workbook(self.filepath)# 获取当前sheetself.ws = self.wb.activedef get_data_from_excel(self):'''从excel获取测试数据'''datas = []for row in range(1, self.ws.max_row + 1):row_data = {}if row > 1:# 将行数据转为字典数据格式for column in range(1, self.ws.max_column + 1):row_data[f"{self.ws.cell(1, column).value}"] = self.ws.cell(row, column).value# 将json_path转字典if row_data["json_path"]:row_data["json_path"] = {k: v for item in row_data["json_path"].split("&") for k, v in[item.split("=")]}datas.append(row_data)self.wb.close()return datasdef close(self):# 关闭文件流self.wb.close()

数据文档格式:

(这里目前只支持单 sheet 页读取,后续将扩展成 sheet 名称传参获取方式)

2、requests 请求部分

import requests
class HttpRequest:def __init__(self):self.session = requests.sessions.session()self.global_headers = {"X-Request-Sign": "xxxxxxxxx"}def set_headers(self, path, content_type, headers):# 判断接口是登录时,将Authorization请求头删除if path in ["oauth/oauth/userlogin", "/oauth/oauth/login"] and "Authorization" in self.global_headers.keys():self.global_headers.pop("Authorization")# 判断content_type为json时,添加请求头content_typeif content_type == "json":self.global_headers["Content-Type"] = "application/json;charset=UTF-8"# 判断content_type不为json时,删除请求头content_typeelif content_type != "json" and "Content-Type" in self.global_headers.keys():self.global_headers.pop("Content-Type")# 将接口信息中的请求头更新到global_headers中if headers:self.global_headers.update(headers)def set_token(self, path, res_code, res_json):# 请求接口为登录并且返回成功时,将token附加到headers里if path == "/oauth/oauth/login" and res_code == 200:token = json_path(res_json, "$.data.access_token")self.global_headers["Authorization"] = f"Bearer {token}"def clear_headers(self, headers):# 清除无用的请求头if headers:for i in headers.keys():if i in self.global_headers.keys():self.global_headers.pop(i)def http_request(self, datas, file=None):log_info.log_info('--------------------')log_info.log_info('↓↓↓↓↓接口请求开始↓↓↓↓↓')# 拼接url  base从配置文件获取+参数传递接口路径urls = config.get_strValue('api', 'base_url') + datas["path"]self.set_headers(path=datas["path"], content_type=datas["content_type"], headers=datas["headers"])log_info.log_info(f'接口名称:{datas["desc"]}')log_info.log_info(f"请求地址:{urls}")log_info.log_info(f"请求头:{self.global_headers}")log_info.log_info(f'请求参数:{datas["params"]}')print((f"请求地址:{urls}"))res = None# 请求封装,这里用的session会话保持,https请求需要将verify设置为Falsetry:if datas["method"].lower() == 'get':res = self.session.request(datas["method"], urls, params=datas["params"], headers=self.global_headers,verify=False)elif datas["method"].lower() == 'post':if datas["content_type"] in ["form", "upload"]:res = self.session.request(datas["method"], urls, data=datas["params"], headers=self.global_headers,files=file,verify=False)elif datas["content_type"] == "json":res = self.session.request(datas["method"], urls, json=datas["params"], headers=self.global_headers,verify=False)log_info.log_info(f"响应信息:{res.text}")except Exception as e:log_info.log_info(e)raise eself.set_token(path=datas["path"], res_code=res.status_code, res_json=res.json())self.clear_headers(headers=datas["headers"])log_info.log_info('↑↑↑↑↑接口请求结束↑↑↑↑↑')log_info.log_info('--------------------')return resdef close(self):self.session.close()

在这里我根据项目情况,进行了请求头的特殊处理;
①实例化对象的时候添加了全局请求头
②根据不同类型的请求和传参格式对 content-type 做了相应处理
③token 的处理
④最后请求后把当前无用请求头部分做了清理

3、接口数据处理

class DataHandle:def datas_init(self,datas,params_list):self.datas = datasself.params_list = params_listdef headers_handle(self):# 将请求头进行参数化if self.datas["headers"]:self.datas["headers"] = re_replace(self.datas["headers"], self.params_list)# 请求头转字典格式if self.datas["headers"]:self.datas["headers"] = {k: v for item in self.datas["headers"].split("&") for k, v in [item.split("=", 1)]}return self.datas["headers"]def params_handle(self):# 将请求参数进行参数化if self.datas["params"]:self.datas["params"] = re_replace(self.datas["params"], self.params_list)# 这里把请求参数格式化,转成不同方法需要的参数格式# 将form表单格式的参数转化成字典if self.datas["content_type"] in ["form","upload"] and self.datas["params"]:self.datas["params"] = {k: v for item in self.datas["params"].split("&") for k, v in [item.split("=")]}# 将json字符串解包为字典elif self.datas["content_type"] == "json":self.datas["params"] = eval(self.datas["params"])return self.datas["params"]def user_var_handle(self,res):# 获取响应信息中下文接口用到的参数if self.datas["json_path"]:for i in self.datas["json_path"].keys():self.params_list[i] = json_path(res.json(), self.datas["json_path"][i])return self.params_listdef assert_handle(self,res):# 将预期结果处理if self.datas["except"]:# 参数化替换self.datas["except"] = re_replace(self.datas["except"], self.params_list)# 转成字符串列表self.datas["except"] = self.datas["except"].split("&")# 断言for i in self.datas["except"]:print(f"断言:{i}")assert i in res.textdef send_request(self,datas,file=None):res = http_request.http_request(datas,file)return res

这里主要做了几点处理:
①将请求头部分做了参数化替换并进行了数据格式转换,转为字典格式
②请求 body 进行参数化,并根据请求方式和 content-type 做了相应处理
③用户参数处理,用 jsonpath 获取响应信息字段值存放到测试类属性中
④预期结果断言处理(这里统一用的自带的 assert 方法,还没进行二次封装)
⑤将更新后的 data 发送请求

4、测试类,测试方法

class TestDemo(DataHandle):params_list = {'custName': unique_string(), "custCertiNo": random_string(),"custLegalPersonCertiNo": random_string(18), "custBankAccount": random_string(18),"managerStamp":{"file": ("testpicture.png", open(upload_file + "\\" + "testpicture.png", "rb"), "image/png")}}@ pytest.mark.usefixtures("login_guanliren")@ pytest.mark.parametrize("datas", DoExcel("demo").get_data_from_excel())def test_case(self, datas):# 数据初始化self.datas_init(datas, self.params_list)# 请求头处理datas["headers"] = self.headers_handle()# 请求参数处理datas["params"] = self.params_handle()# 获取文件名filename = datas["filename"]# 发送接口请求res = self.send_request(datas=datas,file=self.params_list[filename] if datas["content_type"] == "upload" else None)# 参数列表处理self.params_list = self.user_var_handle(res)# 预期结果及断言self.assert_handle(res)

这里测试类继承了上面的 DataHandle 类,测试方法中可以调用父类方法进行测试数据的处理
params_list 是用字典存放的参数化的数据,我这里是通过正则替换把请求参数里面的需要被替换的部分找到 params_list 里的 key,将 value 进行替换

5、runner 执行测试用例

import pytest
from project.utils.base_dir import *if __name__ == '__main__':pytest.main(['-vs', f'{test_class}', f'--alluredir={allure_dir} ', '--clean-alluredir'])os.system(f"allure generate {allure_dir} -o {allure_html} --clean")

调用 pytest 的 main 方法进行命令行参数执行,添加了 allure 生成测试报告

6、测试报告展示

7、未完成功能

①excel 读取:现在只支持当前 sheet 页读取,后续扩展成传 sheet 名称指定获取
②数据库断言:excel 添加一列存放 sql 语句,发送接口请求后查询数据库,将这部分加进断言里
③http_requests 目前只有 get 和 post 方法,后续将其他方法也封装进去
④断言方式:目前没有对断言封装,用的自带的 assert 函数,后面可以进行二次封装或用第三方库实现

最后希望可以给一些想要入手自动化的同学们一些参考吧。

总结

 感谢每一个认真阅读我文章的人!!!

那么在这里我也精心准备了软件测试、自动化测试的详细资料包含:电子书,简历模块,各种工作模板,面试宝典,自学项目等。需要的点击下方名片加入群聊与我一起学习交流。谢谢大家。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/226657.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode(60)K 个一组翻转链表【链表】【困难】

目录 1.题目2.答案3.提交结果截图 链接: K 个一组翻转链表 1.题目 给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。 k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xf…

Modbus转Profinet网关使用方法

Modbus转Profinet网关(XD-MDPN100/200)是用于将Modbus协议和Profinet协议进行转换并进行通迅的设备。Modbus转Profinet网关(XD-MDPN100/200)无论是新项目还是改造项目都可轻松配置完成通迅互联。 正确的安装和配置对于确保设备的正…

低代码核心能力详解:简化应用开发的新思路

低代码平台作为一种快速地应用开发解决方法,为中小企业实现数字化转型提供了机会。但是,对于一些刚开始触碰低代码平台的企业来说,了解其核心能力是很重要的。本文将详细分析低代码平台的核心能力,并在挑选低代码平台以前为中小企…

云原生之深入解析OOM和CPU节流

一、前言 使用 Kubernetes 时,内存不足 (OOM) 错误和 CPU 节流是云应用程序中资源处理的主要难题,这是为什么呢?云应用程序中的 CPU 和内存要求变得越来越重要,因为它们与云成本直接相关。通过 limits 和 requests ,可…

航带模式拍完之后用重建大师跑出来的模型是弧形的,怎么处理?

答:空三设置-更多设置-定位方式中选择pos高精度,再跑一下看看。 重建大师是一款专为超大规模实景三维数据生产而设计的集群并行处理软件,输入倾斜照片,激光点云,POS信息及像控点,输出高精度彩色网格模型&a…

k8s-Pod

1、Pod 简介: (1) 概念: Pod 是 Kubernetes 中创建和管理的,最小的可部署的计算单元。Pod中存储了一组(一个或多个)容器,以及怎样运行这些容器的声明,这些容器共享存储、网络和环境&#xff0…

早上好,我的leetcode 【hash】(第二期)

写在前面:坚持才是最难的事情 C代码还是不方便写,改用python了,TAT 文章目录 1.两数之和49. 字母异位词分组128.最长连续序列 1.两数之和 你好,梦开始的地方~ https://leetcode.cn/problems/two-sum/description/?envTypestudy…

《打造第二大脑》—如何构建高效的笔记系统

最近看了一本书,因为我也用Obsidian来记笔记,(Obsidian之前有介绍过Obsidian使用教程(如何构建你的个人知识库,第二大脑))看完这本书后发现里面给的方法跟Obsidian很契合,所以就整理…

STM32单片机输出频率及占空比可调的PWM波

一、测试用环境 STM32F103C8T6&#xff0c;HAL库。 只考虑PWM的频率和占空比两个参数&#xff0c;死区、极性、对齐方式等不做讨论。 二、STM32Cube MX配置 1.PWM原理 上图中&#xff0c;定时器向上计数&#xff0c;当CNT<CCRx时&#xff0c;输出0&#xff0c;当CNT>C…

linux下的strerror和perror处理错误函数

strerror和perror是C语言中用于处理错误信息的函数。 strerror函数&#xff1a; strerror函数用于将错误码转换为对应的错误消息字符串。它接受一个整数参数&#xff0c;通常是由系统调用或库函数返回的错误码&#xff0c;然后返回一个描述该错误的字符串。 函数原型&#xff1…

PrimDiffusion:3D 人类生成的体积基元扩散模型NeurIPS 2023

NeurIPS2023 &#xff0c;这是一种用于 3D 人体生成的体积基元扩散模型&#xff0c;可通过离体拓扑实现明确的姿势、视图和形状控制。 PrimDiffusion 对一组紧凑地代表 3D 人体的基元执行扩散和去噪过程。这种生成建模可以实现明确的姿势、视图和形状控制&#xff0c;并能够在…

化学方程式小程序

brief introduction 相信大家上中学时都会被化学方程式折腾得死去活来&#xff0c;尤其是配平&#xff0c;怎么也算不对数字。于是我写出了这款近200行的自动配平程序&#xff0c;这是不是你们黑暗化学中的一丝光亮呢&#xff1f; usage 正常化学式输入&#xff0c;每一种物…

卷积的计算 - numpy的实现 2

卷积的计算 - numpy的实现 2 flyfish PyTorch计算 用于验证结果 import torchconv torch.nn.Conv2d(in_channels1, out_channels1, kernel_size2, stride1)# 定义一个卷积核 W torch.arange(1, 5).view(-1, 1, 2, 2).float()# 创建一个二维图像数据&#xff08;4x4 像素&a…

Ray RLlib User Guides:模型,处理器和动作分布

Ray RLlib用户手册地址 默认模型配置设置 在下面的段落中&#xff0c;我们将首先描述RLlib自动构建模型的默认行为&#xff08;如果您没有设置自定义模型&#xff09;&#xff0c;然后深入了解如何通过更改这些设置或编写自己的模型类来自定义模型。 默认情况下&#xff0c;…

D33|动态规划!启程!

1.动态规划五部曲&#xff1a; 1&#xff09;确定dp数组&#xff08;dp table&#xff09;以及下标的含义 2&#xff09;确定递推公式 3&#xff09;dp数组如何初始化 4&#xff09;确定遍历顺序 5&#xff09;举例推导dp数组 2.动态规划应该如何debug 找问题的最好方式就是把…

软件测试面试八股文(答案解析+视频教程)

1、B/S架构和C/S架构区别 B/S 只需要有操作系统和浏览器就行&#xff0c;可以实现跨平台&#xff0c;客户端零维护&#xff0c;维护成本低&#xff0c;但是个性化能力低&#xff0c;响应速度较慢。 C/S响应速度快&#xff0c;安全性强&#xff0c;一般应用于局域网中&#xf…

Yoast SEO Premium v21.7 Premium WordPress 插件Yoast +子插件介绍

什么是Yoast SEO&#xff1f; Yoast SEO 是 WordPress 的 SEO 插件&#xff0c;可帮助网站所有者针对搜索引擎优化他们的网站。该插件由 Joost de Valk 于 2008 年创建&#xff0c;旨在让每个人都能访问 SEO。今天&#xff0c;Yoast SEO 是全球数百万 WordPress 用户使用的最受…

如何对比云渲染平台优劣?到底哪一家好一些?

云渲染作为共享经济的一种体现&#xff0c;在强大计算资源共享的趋势下对个人用户和渲染团队的作品制作提供了巨大的便利。云渲染服务让CG艺术家无需自己投资昂贵的硬件设备&#xff0c;而可以通过网络提交渲染任务到云平台并利用其强大的共享计算资源&#xff0c;不但有效节约…

利用腾讯微搭平台连接MYSQL

首先&#xff0c;找到控制台 然后再控制台搜索&#xff1a;微搭 然后点击进入&#xff1a; 这里如果是第一次进入&#xff0c;他应该会提示要创建环境。 然后按照这个步骤&#xff1a; 然后进入这个页面&#xff0c;点击编辑器&#xff1a; 然后在这里搜索表格&#xff1a; 点…

Docker常用管理命令

Docker常用管理命令 一、Docker常用管理命令 docker ps 功能&#xff1a;查看正在运行的容器列表。示例&#xff1a;docker ps -a&#xff08;查看所有容器&#xff0c;包括已停止的容器&#xff09;。 docker start <container_id> 功能&#xff1a;启动一个已停止的容…