excel+requests管理测试用例接口自动化框架

背景:

某项目有多个接口,之前使用的unittest框架来管理测试用例,将每个接口的用例封装成一个py文件,接口有数据或者字段变动后,需要去每个py文件中找出变动的接口测试用例,维护起来不方便,为了便于接口变动后维护,使用excel来管理测试用例,接口有变动不需要修改代码,只需要维护excel即可。

思路:

为了方便维护测试用例,一个接口的测试用例使用一个excel文件来管理,每个excel文件中有两个sheet页,第一个sheet页是接口的基本信息,包括接口名称,地址和请求方式,第二个sheet页为接口的测试用例,如下图所示

第一个sheet页

第二个sheet页

 接口请求的数据类型为X-WWW-FORM-URLENCODED,在测试用例中每个字段为一列,每条用例为一行,倒数第二列为预期结果,倒数第三列为该条用例的描述。

接口自动化框架结构:

common目录存放公共的方法,例如写日志,连数据库

config目录存放配置文件和读取配置文件内容的方法,cfg.ini包括发送邮件的配置信息和接口的ip和端口

data目录存放接口的测试用例

logs目录存放用例执行的日志

report目录存放测试报告

run_main.py为用例执行的入口

源码:api_test.py封装读取测试用例的数据,执行测试用例和校验测试结果


#coding:utf-8import xlrd,os
import requests
from datetime import datetime
from xlrd import xldate_as_tuple
from config import readConfig
from common.logger import Log'''
获取测试用例data所在的目录
'''
d = os.path.dirname(__file__) #返回当前文件所在目录(common文件夹路径)
parent_path = os.path.dirname(d) #返回common的父级目录
data_path = os.path.join(parent_path,'data') #返回data所在目录
data_path1 = os.listdir(data_path) #返回data目录下所有的文件log = Log()def api_data():for filename in data_path1:book = xlrd.open_workbook(os.path.join(data_path,filename))'''获取excel文件中接口信息'''table = book.sheet_by_index(0) #通过索引,获取相应的列表,这里表示获取excel的第一个列表inf_name = table.row_values(1)[0] #返回接口名称inf_address = table.row_values(1)[1] #返回接口地址inf_mode = table.row_values(1)[2] #返回请求方式'''获取excel文件中测试用例信息'''sheet = book.sheet_by_index(1) #通过索引,获取相应的列表,这里表示获取excel的第二个列表nrows = sheet.nrows #获取所有行数filed = sheet.row_values(0)# print(filed)for i in range(1,nrows):d1 = {}for j in range(0,len(filed)-2):ctype = sheet.cell(i, j).ctype  # 表格的数据类型cell = sheet.cell_value(i, j)d = {}if ctype == 2 and cell % 1 == 0:  # 如果是整形cell = int(cell)elif ctype == 3:# 转成datetime对象date = datetime(*xldate_as_tuple(cell, 0))cell = date.strftime('%Y/%m/%d')elif ctype == 4:cell = True if cell == 1 else False# print(cell)d.update({filed[j]:cell})# print(d)d1.update(d)# print(d1)'''获取excel文件中测试用例预期结果和描述'''a = []for k in range(len(filed)-2,len(filed)):ctype = sheet.cell(i, k).ctype  # 表格的数据类型cell = sheet.cell_value(i, k)if ctype == 2 and cell % 1 == 0:  # 如果是整形cell = int(cell)elif ctype == 3:# 转成datetime对象date = datetime(*xldate_as_tuple(cell, 0))cell = date.strftime('%Y/%m/%d')elif ctype == 4:cell = True if cell == 1 else Falsea.append(cell)# print(a[0])# print(type(a[0]))'''获取cfg.ini配置文件中接口公共信息(ip和port)'''ip = readConfig.ip  # 获取配置文件中接口ipi_port = readConfig.i_port  # 获取配置文件中接口porturl = "http://" + ip + ":" + i_port + inf_addressheaders = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0","X-Requested-With": "XMLHttpRequest","Connection": "keep-alive"}par = d1'''判断请求方式是GET还是POST,并且判断测试用例预期结果与实际响应一致'''if inf_mode == 'GET':r = requests.get(url, params=par)result = r.json()# log.info("---编号%s,接口名称%s---")%(i,inf_name)print(inf_name, str(result).replace('None','null'), a[1])if str(result).replace('None','null') == a[0]:log.info("pass")log.info("--------")else:log.info("false")log.info("--------")elif inf_mode == 'POST':r = requests.post(url, data=par, headers=headers)result = r.json()print(inf_name, str(result).replace('None', 'null'), a[1])if str(result).replace('None', 'null') == a[0]:print('pass')print('--------')else:print('false')print('--------')api_data()

配置文件cfg.ini(主要配置邮箱和接口ip+port等常用数据信息)


[email]smtp_server = smtp.163.com
port = 465
sender = xxx;psw是QQ邮箱的授权码
psw = xxx;收件人多个时,中间用逗号隔开,如'a@xx.com,b@xx.com'
receiver = xxx[interface]ip = xxx
;接口ip
port = xxx
;接口端口

readConfig.py读取配置文件中数据


# coding:utf-8
import os
import configparsercur_path = os.path.dirname(os.path.realpath(__file__))
configPath = os.path.join(cur_path, "cfg.ini")
conf = configparser.ConfigParser()
conf.read(configPath,encoding='utf-8')smtp_server = conf.get("email", "smtp_server")sender = conf.get("email", "sender")psw = conf.get("email", "psw")receiver = conf.get("email", "receiver")port = conf.get("email", "port")ip = conf.get("interface","ip")i_port = conf.get("interface","port")

优化:

部分接口访问时,响应未知用户,需要用session关联接口,先调用登录接口,把登录接口的调用封装成了一个实例方法,实现了复用,登录之后,登录接口的http响应会把session以 cookie的形式set到客户端,之后的接口都会使用此session去请求封装登录接口user_login.py


#coding:utf-8
import requests
from common.logger import Logclass Login():log = Log()def __init__(self,s):self.s = sdef login(self,code,passwd):url = "http://192.168.20.100:8081/backend/system/user/login"headers = {"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8","User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36","X-Requested-With":"XMLHttpRequest","Cookie":"JSESSIONID=92D7FB4C7FB917B7D2E8DC429A63443F","Connection":"keep-alive"}d = {"code":code,"passwd":passwd}res = self.s.post(url,headers=headers,data=d)result1 = res.text #字节输出self.log.info(u"调用登录方法,获取结果:%s"%result1)return res.json()

优化api_test.py中部分代码(红色部分为优化的代码)

1.在请求接口前首先调用登录接口2.加入执行用例的编号(p),每循环一次自增1


#coding:utf-8import xlrd,os
import requests
from datetime import datetime
from xlrd import xldate_as_tuple
from config import readConfig
from common.logger import Log
from case.user_login import Login'''
获取测试用例data所在的目录
'''
d = os.path.dirname(__file__) #返回当前文件所在目录(common文件夹路径)
parent_path = os.path.dirname(d) #返回common的父级目录
data_path = os.path.join(parent_path,'data') #返回data所在目录
data_path1 = os.listdir(data_path) #返回data目录下所有的文件s = requests.session()
lon = Login(s)
log = Log()def api_data():p = 1for filename in data_path1:book = xlrd.open_workbook(os.path.join(data_path,filename))'''获取excel文件中接口信息'''table = book.sheet_by_index(0) #通过索引,获取相应的列表,这里表示获取excel的第一个列表inf_name = table.row_values(1)[0] #返回接口名称inf_address = table.row_values(1)[1] #返回接口地址inf_mode = table.row_values(1)[2] #返回请求方式'''获取excel文件中测试用例信息'''sheet = book.sheet_by_index(1) #通过索引,获取相应的列表,这里表示获取excel的第二个列表nrows = sheet.nrows #获取所有行数filed = sheet.row_values(0)# print(filed)for i in range(1,nrows):d1 = {}for j in range(0,len(filed)-2):ctype = sheet.cell(i, j).ctype  # 表格的数据类型cell = sheet.cell_value(i, j)d = {}if ctype == 2 and cell % 1 == 0:  # 如果是整形cell = int(cell)elif ctype == 3:# 转成datetime对象date = datetime(*xldate_as_tuple(cell, 0))cell = date.strftime('%Y/%m/%d')elif ctype == 4:cell = True if cell == 1 else False# print(cell)d.update({filed[j]:cell})# print(d)d1.update(d)# print(d1)'''获取excel文件中测试用例预期结果和描述'''a = []for k in range(len(filed)-2,len(filed)):ctype = sheet.cell(i, k).ctype  # 表格的数据类型cell = sheet.cell_value(i, k)if ctype == 2 and cell % 1 == 0:  # 如果是整形cell = int(cell)elif ctype == 3:# 转成datetime对象date = datetime(*xldate_as_tuple(cell, 0))cell = date.strftime('%Y/%m/%d')elif ctype == 4:cell = True if cell == 1 else Falsea.append(cell)# print(a[0])# print(type(a[0]))'''获取cfg.ini配置文件中接口公共信息(ip和port)'''ip = readConfig.ip  # 获取配置文件中接口ipi_port = readConfig.i_port  # 获取配置文件中接口porturl = "http://" + ip + ":" + i_port + inf_addressheaders = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0","X-Requested-With": "XMLHttpRequest","Connection": "keep-alive"}par = d1'''判断请求方式是GET还是POST,并且判断测试用例预期结果与实际响应一致,所有接口请求前先调用登录接口'''code = "xxx"  #登录接口用户codepasswd = "xxx" #登录接口用户passwdlon.login(code, passwd)if inf_mode == 'GET':r = s.get(url, params=par)result = r.json()log.info("编号:%s,接口名称:%s,测试点:%s,响应:%s"%(p,inf_name,a[1],str(result).replace('None','null')))# print(inf_name, str(result).replace('None','null'), a[1])if str(result).replace('None','null') == a[0]:log.info("pass")log.info("--------")else:log.info("false")log.info("--------")elif inf_mode == 'POST':r = s.post(url, data=par, headers=headers)result = r.json()log.info("编号:%s,接口名称:%s,测试点:%s,响应:%s" % (p, inf_name,a[1],str(result).replace('None', 'null')))# print(inf_name, str(result).replace('None', 'null'), a[1])if str(result).replace('None', 'null') == a[0]:log.info("pass")log.info("--------")else:log.info("false")log.info("--------")p=p+1api_data()

执行结果:

优化二:

excel中添加结果列,将每条用例的执行结果写入excel中,因为excel版本是2007以上,采用openpyxl模块去修改excel单元格的值,执行通过用绿色字体标注pass,执行不通过的用例红色字体标注false优化api_test.py中部分代码


#coding:utf-8import xlrd,os
import requests
import openpyxl
from openpyxl.styles import Font
# from xlutils.copy import copy
from datetime import datetime
from xlrd import xldate_as_tuple
from config import readConfig
from common.logger import Log
from case.user_login import Login'''
获取测试用例data所在的目录
'''
d = os.path.dirname(__file__) #返回当前文件所在目录(common文件夹路径)
parent_path = os.path.dirname(d) #返回common的父级目录
data_path = os.path.join(parent_path,'data') #返回data所在目录
data_path1 = os.listdir(data_path) #返回data目录下所有的文件s = requests.session()
lon = Login(s)
log = Log()def api_data():p = 1for filename in data_path1:book = xlrd.open_workbook(os.path.join(data_path,filename))'''使用xlwt操作excel,xlwt只支持excel2007以下版本'''# wb = copy(book)# ws = wb.get_sheet(1)'''使用openpyxl操作excel,openpyxl支持excel2007以上版本'''wb = openpyxl.load_workbook(os.path.join(data_path,filename))ws = wb.worksheets[1]font_green = Font(color="37b400")font_red = Font(color="ff0000")'''获取excel文件中接口信息'''table = book.sheet_by_index(0) #通过索引,获取相应的列表,这里表示获取excel的第一个列表inf_name = table.row_values(1)[0] #返回接口名称inf_address = table.row_values(1)[1] #返回接口地址inf_mode = table.row_values(1)[2] #返回请求方式'''获取excel文件中测试用例信息'''sheet = book.sheet_by_index(1) #通过索引,获取相应的列表,这里表示获取excel的第二个列表nrows = sheet.nrows #获取所有行数ncols = sheet.ncols #获取所有列数filed = sheet.row_values(0)# print(filed)for i in range(1,nrows):d1 = {}for j in range(0,len(filed)-3):ctype = sheet.cell(i, j).ctype  # 表格的数据类型cell = sheet.cell_value(i, j)d = {}if ctype == 2 and cell % 1 == 0:  # 如果是整形cell = int(cell)elif ctype == 3:# 转成datetime对象date = datetime(*xldate_as_tuple(cell, 0))cell = date.strftime('%Y/%m/%d')elif ctype == 4:cell = True if cell == 1 else False# print(cell)d.update({filed[j]:cell})# print(d)d1.update(d)# print(d1)'''获取excel文件中测试用例预期结果和描述'''a = []for k in range(len(filed)-3,len(filed)-1):ctype = sheet.cell(i, k).ctype  # 表格的数据类型cell = sheet.cell_value(i, k)if ctype == 2 and cell % 1 == 0:  # 如果是整形cell = int(cell)elif ctype == 3:# 转成datetime对象date = datetime(*xldate_as_tuple(cell, 0))cell = date.strftime('%Y/%m/%d')elif ctype == 4:cell = True if cell == 1 else Falsea.append(cell)# print(a[0])# print(type(a[0]))'''获取cfg.ini配置文件中接口公共信息(ip和port)'''ip = readConfig.ip  # 获取配置文件中接口ipi_port = readConfig.i_port  # 获取配置文件中接口porturl = "http://" + ip + ":" + i_port + inf_addressheaders = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0","X-Requested-With": "XMLHttpRequest","Connection": "keep-alive"}par = d1'''判断请求方式是GET还是POST,并且判断测试用例预期结果与实际响应一致,所有接口请求前先调用登录接口'''code = "xuxingan"passwd = "admin"lon.login(code, passwd)if inf_mode == 'GET':r = s.get(url, params=par)result = r.json()log.info("编号:%s,接口名称:%s,测试点:%s,响应:%s"%(p,inf_name,a[1],str(result).replace('None','null')))# print(inf_name, str(result).replace('None','null'), a[1])if str(result).replace('None','null') == a[0]:# ws.write(i,ncols-1,'pass'.encode('utf-8'))ws.cell(row=i+1,column=ncols,value='pass').font = font_greenlog.info("pass")log.info("--------")else:# ws.write(i,ncols-1,'false'.encode('utf-8'))ws.cell(row=i+1, column=ncols, value='false').font = font_redlog.info("false")log.info("--------")elif inf_mode == 'POST':r = s.post(url, data=par, headers=headers)result = r.json()log.info("编号:%s,接口名称:%s,测试点:%s,响应:%s" % (p, inf_name,a[1],str(result).replace('None', 'null')))# print(inf_name, str(result).replace('None', 'null'), a[1])if str(result).replace('None', 'null') == a[0]:# ws.write(i,ncols-1,'pass'.encode('utf-8'))ws.cell(row=i+1, column=ncols, value='pass').font = font_greenlog.info("pass")log.info("--------")else:# ws.write(i,ncols-1,'false'.encode('utf-8'))ws.cell(row=i+1, column=ncols, value='false').font = font_redlog.info("false")log.info("--------")wb.save(os.path.join(data_path, filename))p=p+1log.info("总计%s条用例"%p)api_data()

执行结果

总结:

第一个版本有点粗糙,1.后续加入将每条用例的执行结果写入测试用例excel文件 2.生成自动化测试报告

Python接口自动化测试零基础入门到精通(2023最新版)

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

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

相关文章

Spring AOP归纳与总结

前言 AOP的核心思想是面向切面编程。AOP规范定义了多种概念,常用的aop框架有spring aop和AspectJ,两者功能和性能差异较大,现在默认的AOP框架是AspectJ,下面逐渐归纳其相关概念、功能及实现原理。 1. 概念 1. 切面:…

从零开始学习 Java:简单易懂的入门指南之线程池(三十六)

线程池 1.1 线程状态介绍1.2 线程池-基本原理1.3 线程池-Executors默认线程池1.4 线程池-Executors创建指定上限的线程池1.5 线程池-ThreadPoolExecutor1.6 线程池-参数详解1.7 线程池-非默认任务拒绝策略 1.1 线程状态介绍 当线程被创建并启动以后,它既不是一启动…

linux文件权限与目录配置

用户与用户组 linux一般将文件可读写的身份分为三个类别:拥有者(owner)、所属群组(group)、其他人(other) 三种身份都有读、写、执行等权限 文件拥有者 linux是个多人多任务的系统&#xff0c…

论文解析-moETM

论文解析-moETM 参考亮点动机发展现状现存问题 功能方法Encoder改进Decoder改进 评价指标生物保守性批次效应移除 实验设置结果多组学数据整合cell-topic mixture可解释性组学翻译性能评估RNA转录本、表面蛋白、染色质可及域调控关系研究1. 验证同一主题下,top gene…

什么是NetApp的DQP和如何安装DQP?

首先看看什么是DQP,DQPDisk Qualification Package,文字翻译就是磁盘验证包。按照NetApp的最佳实践,要定期升级DQP包,保证对最新磁盘和磁盘扩展柜的兼容。 本文主要介绍7-mode下如何升级DQP,至于cluster mode另外文章…

NewStarCTF2023week2-Upload again!

尝试传修改后缀的普通一句话木马&#xff0c;被检测 尝试传配置文件 .htaccess 和 .user.ini 两个都传成功了 接下来继续传入经过修改的木马 GIF89a <script language"php"> eval($_POST[cmd]); </script> 没有被检测&#xff0c;成功绕过 直接上蚁剑…

圣树唤歌最强阵容2023,圣树唤歌阵容推荐

无疑圣树唤歌作为一款备受欢迎的手机游戏&#xff0c;其深刻的战斗系统一直以来都受到大家的追捧。在这个虚拟世界中胜利的关键在于组建一支无懈可击的强大队伍&#xff0c;要想成为强者&#xff0c;就必须拥有最强阵容。 关注【娱乐天梯】&#xff0c;获取内部福利号 在本篇攻…

C++项目实战——基于多设计模式下的同步异步日志系统(总集篇)

文章目录 专栏导读项目介绍开发环境核心技术环境搭建日志系统介绍1.为什么需要日志系统2.日志系统技术实现2.1同步写日志2.2异步写日志 前置知识补充不定参函数C风格不定参函数不定参宏函数设计模式六大原则单例模式饿汉模式懒汉模式 工厂模式简单工厂模式工厂方法模式抽象工厂…

Linux:mongodb数据逻辑备份与恢复(3.4.5版本)

我在数据库aaa的里创建了一个名为tarro的集合&#xff0c;其中有三条数据 备份语法 mongodump –h server_ip –d database_name –o dbdirectory 恢复语法 mongorestore -d database_name --dirdbdirectory 备份 现在我要将aaa.tarro进行备份 mongodump --host 192.168.254…

攻防演练蓝队|Windows应急响应入侵排查

文章目录 日志分析web日志windows系统日志 文件排查进程排查新增、隐藏账号排查启动项/服务/计划任务排查工具 日志分析 web日志 dirpro扫描目录&#xff0c;sqlmap扫描dvwa Python dirpro -u http://192.168.52.129 -b sqlmap -u "http://192.168.52.129/dvwa/vulnera…

Jmeter接口测试:jmeter导入和导出接口的处理

JMeter测试导入接口 利用Jmeter测试上传文件&#xff0c;首先可根据接口文档或者fiddler抓包分析文件上传的接口&#xff1b;如下图&#xff1a; 以下是我通过fiddler所截取的文件上传的接口 1、填写导入接口的信息 查看文件上传栏下的填写信息&#xff1a; 文件名称&#x…

Unity中Shader的深度写入ZWrite

文章目录 前言一、更新深度缓冲区中值二、深度值的写入操作只有两个选择 开启 和 关闭ZWrite OnZWrite Off 三、深度写入在半透明物体物体中开启的情况1、特效一般都需要关闭深度写入2、如果在人物模型上使用 特效半透明 的 Shader&#xff0c;为了不出现模型自身穿透问题&…

Web3D虚拟人制作简明指南

如何在线创建虚拟人? 虚拟人,也称为数字化身、虚拟助理或虚拟代理,是一种可以通过各种在线平台与用户进行逼真交互的人工智能人。 在线创建虚拟人变得越来越流行,因为它为个人和企业带来了许多好处。 通过虚拟助理或代理,您可以以更具吸引力和个性化的方式与客户或受众进…

146.LRU缓存

双向链表哈希表 class LRUCache { public://1、定义双向链表结构、容量、哈希表等LRU数据成员struct Node{int key,value;Node *left,*right;Node(int _key,int _value):key(_key),value(_value),left(NULL),right(NULL){}}*L,*R;int n;unordered_map<int,Node*> ump;//…

李航:关于大模型的思考及研究热点

本文阐述李航老师对 LLM 的一些看法&#xff0c;主要观点如下&#xff1a; ChatGPT 的突破主要在于规模带来的质变和模型调教方式的发明。 LLM 融合了实现人工智能的三条路径。 LLM 的开发需要结合第三者体验和第一者体验。 LLM 能近似生成心智语言。 LLM 需要与多模态大模…

SamSung三星笔记本NP930QCG-K02CN原装出厂OEM预装Win10系统

下载链接&#xff1a;https://pan.baidu.com/s/13GsR_r9caJkLjiWWaXa30Q?pwdncp9 系统自带指纹驱动、声卡,网卡,显卡等所有驱动、三星出厂时主题壁纸、系统属性三星专属LOGO标志、Office办公软件等三星出厂时自带的预装程序 由于时间关系,绝大部分资料没有上传&#xff0c;不…

Unity之ShaderGraph如何实现上下溶解

前言 我们经常在电影中见到的一个物体或者人物&#xff0c;从头上到脚下&#xff0c;慢慢消失的效果&#xff0c;我么今天就来体验一下这个上下溶解。 主要节点 Position节点&#xff1a;提供对网格顶点或片段的Position 的访问 Step节点&#xff1a;如果输入In的值大于或…

双目视觉实战--相机几何

目录 一、针孔摄像机和透镜 1. 针孔摄像机的原理 2. 近轴折射模型 3. 镜头畸变问题 二、摄像机几何 1. 数学基础 2. 相机坐标系&#xff08;空间点&#xff09;→像素坐标系的映射关系&#xff1a; 3. 规范化投影变换 4. 投影变换的性质 三、其他摄像机模型 1. 弱透视…

新时代高效记账:自动化智能如何进行财务管理

随着科技的不断发展&#xff0c;自动化智能已经逐渐渗透到我们生活的各个领域。在财务管理中&#xff0c;自动化智能的应用显得尤为重要。它不仅可以提高财务管理的效率和精度&#xff0c;还能帮助我们更好地规划和掌控公司的财务状况 晨曦记账本提供了多种高效财务管理工具。…

【MultiOTP】Docker安裝MultiOTP, 让Windows登入更安全(MFA)

序 在当前数字时代&#xff0c;网络安全成为了一个非常重要的话题。随着越来越多的人和组织依赖于计算机系统来进行工作和存储敏感信息&#xff0c;确保身份验证安全变得至关重要。双因素身份验证&#xff08;2FA&#xff09;是一种强大的安全措施&#xff0c;可在传统的用户名…