文章目录
关于Pytest和Allure如何使用请查看
此文章,这里不再介绍
Requests库介绍
requests库是Python第三方库,用于模拟浏览器向服务器发出请求,比如:常用的
GET
,
POST
,
PUT
,
DELETE
等请求,发起对应请求可以使用
requests.get()……
,此方式更适合测试少量接口时使用,通常使用
requests.request(method,url,**kwargs)
将请求方法作为参数,更便于做接口自动化,下文也将以此种方式进行介绍
Requests库安装
因为是Python第三方库,需要单独安装,方法如下
pip install requests # 通过官网安装,国内环境可能会下载失败,可以使用国内pip源
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple # 临时使用清华pip源
或者永久更改pip源后再执行
pip install requests
,执行以下命令将永久更改为清华pip源
pip config setglobal.index-url https://pypi.tuna.tsinghua.edu.cn/simple
Requests库使用
安装后需导入requests库使用,如下示例:
import requests # # 导入requests库
base_url ="http://192.166.66.24:8090"# 为方便测试,先定义个base_url,也可以跟路径写一起
login_url = base_url+"/api/admin/login"# 登录URL,通过base_url与路径拼接
login_data ={"username":"admin","password":"admin123","authcode":None}# 登录需要的入参数据
r_login = requests.request(method='post',url=login_url,json=login_data)# 发起post请求print(r_login.status_code)# 打印响应状态码print(r_login.content)# 打印原始响应体,即raw格式的
ck = r_login.content # 获取cookie值
token = r_login.json()['data']['access_token']# 获取响应中的token值print(token)# 打印token值
artlist_url = base_url+"/api/admin/posts"# 获取文章列表的URL
header={"Admin-Authorization":token}# 请求头信息,需要带上token值
r_artlist = requests.request("get",artlist_url,headers=header)# 发起get请求,method和url可省略参数名print(r_artlist.json())# 打印json格式的响应数据print(r_artlist.text)# 打印字符串格式的响应数据
**requests.request(method,url,kwargs)参数说明
method:请求方法,如GET、POST、PUT、DELETE等请求
url:请求的URL地址
**kwargs:是一个可变的参数类型,在传实参时,以关键字参数的形式传入,python会自动解析为字典的形式,常用可选参数如下:
- params:字典或元组列表或字节,作为参数增加到url中
- data:字典,元组列表,字节或文件对象,作为post、put等请求的参数
- json:JSON格式的数据,作为post、put等请求的参数
- headers:字典类型, HTTP请求头信息
- cookies:字典或CookieJar,Request中的auth : 元组支持HTTP认证功能
- files:字典类型,传输文件,作为post请求文件流数据,测试文件上传接口时使用
- timeout:设定超时时间,单位为秒
params、data、json区别:
在接口请求中,使用哪一个参数需要根据接口请求方法和编码格式而定,三者的大致区别如下:
- params:通常作为get请求中接收参数,
params=字典类型的数据
- json:通常作为post、put、delete等请求中接收参数,
json=字典格式的数据
(content_type
为application/json
时使用) - data:与json一样,作为请求中的接收参数,
data=字典格式的数据
(content_type
为键值对的编码格式时使用,比如application/x-www-form-urlencoded
)
实际案例
以测试开源项目Halo为例,此项目可以在GitHub上找到,安装到本地虚机进行测试的通过抓包获取接口后即可用来学习,首先需要登录,如下是Halo的登录接口测试
import requests # 导入requests库classTestCom(object):# 定义一个名为TestCom的类,默认继承object基类
base_url ="http://192.166.66.24:8090"# 因为测试环境可能切换,所以可以定义个base_url@classmethod# 使用类方法defsetup_class(cls):# 以为是基于pytest框架,所以使用pytest中的setup,实现操作前先登录
login_url = cls.base_url +"/api/admin/login"# 定义接口地址,由base_url与路径拼接
login_data ={"username":"admin","password":"admin123","authcode":None}# 登录信息
r_login = cls.request(method='post', url=login_url, json=login_data)# 发起post请求
cls.token = r_login.json()['data']['access_token']# 获取响应中的token值
除登录外,其它接口请求头中需要带token值,所以,以类的继承方式进行token传递,将上面的登录请求封装到公共方法中,创建文件
common.py
将登录请求放到此文件中,然后编写其它接口的测试,先创建文件
test_art.py
,然后开始写接口用例
import requests # 导入requestsimport pytest # 导入pytestfrom com.common import TestCom # 导入common.py中的TestCom类classTestArt(TestCom):# 定义一个名为TestArt的类,继承TestCom类deftest_artlist(self):# 这是查看文章列表的接口请求
artlist_url = self.base_url+"/api/admin/posts"# 使用公共方法中base_url与请求路径拼接
header ={"Admin-Authorization": self.token}# 使用基类中的token值
artlist_params ={"page":"1","size":"10"}# get请求中的参数,使用requests中的params参数
r_artlist = requests.request("get",url=artlist_url,headers=header,params=artlist_params)print(r_artlist.json())# 打印JSON格式的响应结果assert r_artlist.json()["message"]=="OK"# 断言,判断响应的信息和状态码是否与预期结果一致assert r_artlist.status_code ==200deftest_artwrite(self):# 这是写文章的接口请求
artwrite_url = self.base_url+"/api/admin/posts"# 使用公共方法中base_url与请求路径拼接
header ={"Admin-Authorization": self.token}
art_data={"title":"无题六首其三",# 请求参数,请求头使用TestArt类中定义的header"originalContent":"直道相思了无益,未妨惆怅是清狂。","status":"DRAFT","keepRaw":True}
r_artwrite = requests.request('POST',artwrite_url,headers=header,json=art_data)
art_id =str(r_artwrite.json()['data']['id'])# 获取响应结果中文章的idglobals()["art_ids"]=art_id # 将art_id设为全局变量,供下面的接口调用assert r_artwrite.json()["message"]=="OK"# 断言assert r_artwrite.status_code ==200deftest_artrecycle(self):# 这是将文章放到回收站的接口请求
artrecycle_url = self.base_url+"/api/admin/posts/"+globals()["art_ids"]+"/status/RECYCLE"
header ={"Admin-Authorization": self.token}
r_artrecycle = requests.request('put',artrecycle_url,headers=header)assert r_artrecycle.json()["message"]=="OK"assert r_artrecycle.status_code ==200deftest_artdelete(self):# 这是将文章从回收站彻底删除的接口请求
artdelete_url = self.base_url +"/api/admin/posts"
artdelete_id =[globals()["art_ids"]]# 请求体需要列表格式,将文章id转换为列表格式后赋值给artdelete_id
header ={"Admin-Authorization": self.token}
r_artdelete = requests.request('delete', url=artdelete_url, headers=header, json=artdelete_id)assert r_artdelete.json()["message"]=="OK"assert r_artdelete.status_code ==200
添加日志功能
测试工作肯定是少不了工作记录的,使用日志记录接口执行情况,创建文件
logger.py
,定义日志信息,如下:
import logging
import os
logger = logging.getLogger("dyd-测试日志")# 定义日志并设置名称然后赋值给logger
logger.setLevel(logging.DEBUG)# 设置日志为DEBUG级别# 定义日志格式,输出格式为:当前时间 - 日志等级 - 函数名 - 日志信息format= logging.Formatter("%(asctime)s - %(levelname)s - %(funcName)s - %(message)s")
yd = logging.StreamHandler()# 输出日志记录
yd.setFormatter(format)# 输出日志使用定义的format格式
logger.addHandler(yd)# 日志输出到控制台# 定义日志存放目录
log = os.path.join(os.path.dirname(__file__),"../logs")# 获取当前路径,返回上一级进入logs目录ifnot os.path.exists(log):# 如果logs目录不存在,就先创建logs目录
os.mkdir(log)
logfiles = os.path.join(log,"APItest.log")
re = logging.FileHandler(logfiles)# 日志记录到指定文件中
re.setFormatter(format)# 输出日志使用定义的format格式
logger.addHandler(re)# 日志输出到控制台
将日志运用到实际项目的接口测试中,需要在任何要保存日志的地方添加要记录的信息,如下示例:
import requests
from com.common import TestCom
from com.logger import logger # 导入定义的日志模块classTestArt(TestCom):deftest_artlist(self):# 这是查看文章列表的接口请求
artlist_url = self.base_url +"/api/admin/posts"
header ={"Admin-Authorization": self.token}
r_artlist = requests.request("get", url=artlist_url, headers=header)
logger.debug(f"响应结果:{r_artlist.text}")# 添加响应结果日志assert r_artlist.json()["message"]=="OK"assert r_artlist.status_code ==200deftest_artwrite(self):# 这是写文章的接口请求
artwrite_url = self.base_url +"/api/admin/posts"
header ={"Admin-Authorization": self.token}
art_data ={"title":"无题六首其三","originalContent":"直道相思了无益,未妨惆怅是清狂。","keepRaw":True}
r_artwrite = requests.request('POST', artwrite_url, headers=header, json=art_data)
logger.info(f"请求头:{header},请求地址:{artwrite_url},响应状态码:{r_artwrite.status_code}")assert r_artwrite.json()["message"]=="OK"assert r_artwrite.status_code ==200
封装requests库
根据实际项目可以自己封装requests库,更便于自动化测试,首先将requests库封装到在
common.py
文件中,并添加日志功能,如下:
import requests
from com.logger import logger # 导入定义的日志模块classTestCom(object):
base_url ="http://192.166.66.24:8090"@classmethoddefsetup_class(cls):
login_url = cls.base_url +"/api/admin/login"
login_data ={"username":"admin","password":"admin123","authcode":None}
r_login = cls.request(method='post', url=login_url, json=login_data)# 使用下面封装的请求
cls.token = r_login.json()['data']['access_token']@classmethoddefrequest(self, method:str, url, params=None, data=None, json=None,**kwargs):# 自定义发送请求"""自定义发送请求
请求方法为字符串格式,params、data、json数据可以为空
method:请求方法
url:请求URL
params:get请求的参数
data:body中的数据
json:body中json格式的数据
kwargs:其它字典参数,允许传入不定长的参数
"""
method = method.upper()# 将请求方法转换成大写if method =="GET":# 当请求方法为GET时,调用requests库中的get请求
res = requests.get(url, params=params,**kwargs)# 输出以下日志信息,可以添加更多信息,比如请求头,响应状态等
logger.info(f"请求方法:{method},请求地址:{url},请求参数:{res.request.body},响应结果:{res.text}")return res # 返回请求elif method =="POST":# 当请求方法为POST时,调用requests库中的POST请求
res = requests.post(url, data=data, json=json,**kwargs)
logger.info(f"请求方法:{method},请求地址:{url},请求参数:{res.request.body},响应结果:{res.text}")return res
elif method =="PUT":# 当请求方法为PUT时,调用requests库中的PUT请求
res = requests.put(url, data=data, json=json,**kwargs)
logger.info(f"请求方法:{method},请求地址:{url},请求参数:{res.request.body},响应结果:{res.text}")return res
elif method =="DELETE":# 当请求方法为DELETE时,调用requests库中的DELETE请求
res = requests.delete(url, data=data, json=json,**kwargs)
logger.info(f"请求方法:{method},请求地址:{url},请求参数:{res.request.body},响应结果:{res.text}")return res
else:# 如果不是以上4种请求方法,则提示"请求方法未定义,请检查!"print("请求方法未定义,请检查!")
上面的登录接口请求使用了自己封装后的请求方法,其它接口测试同样也可以使用自己封装的方法,如下所示:
from com.common import TestCom
import pytest
classTestArt(TestCom):deftest_artlist(self):# 这是查看文章列表的接口请求
artlist_url = self.base_url+"/api/admin/posts"
header ={"Admin-Authorization": self.token}
artlist_params ={"page":"1","size":"10"}
r_artlist = self.request("get",url=artlist_url,headers=header,params=artlist_params)assert r_artlist.json()["message"]=="OK"assert r_artlist.status_code ==200deftest_artwrite(self):# 这是写文章的接口请求
artwrite_url = self.base_url+"/api/admin/posts"
header ={"Admin-Authorization": self.token}
art_data={"title":"无题六首其三","originalContent":"直道相思了无益,未妨惆怅是清狂。","keepRaw":True}
r_artwrite = self.request(method='POST', url=artwrite_url, headers=header, json=art_data)
art_id =str(r_artwrite.json()['data']['id'])globals()["art_ids"]=art_id
assert r_artwrite.json()["message"]=="OK"assert r_artwrite.status_code ==200deftest_artrecycle(self):# 这是将文章放到回收站的接口请求
artrecycle_id=globals()["art_ids"]# 将全局变量进行赋值,下面使用f方式进行字符串拼接
artrecycle_url = self.base_url+f"/api/admin/posts/{artrecycle_id}/status/RECYCLE"
header ={"Admin-Authorization": self.token}
r_artrecycle = self.request('put', url=artrecycle_url, headers=header)assert r_artrecycle.json()["message"]=="OK"assert r_artrecycle.status_code ==200deftest_artdelete(self):# 这是将文章从回收站彻底删除的接口请求
artdelete_url = self.base_url +"/api/admin/posts"
artdelete_id =[globals()["art_ids"]]
header ={"Admin-Authorization": self.token}
r_artdelete = self.request('delete', url=artdelete_url, headers=header, json=artdelete_id)assert r_artdelete.json()["message"]=="OK"assert r_artdelete.status_code ==200if __name__ =='__main__':
pytest.main(["-sv","test_art.py"])
使用pytest装饰器实现参数化
使用参数化完成所有接口请求,不再需要测试一条用例就要定义一个方法,如下所示:
from com.common import TestCom
import pytest
classTestArt(TestCom):# 定义传参,同时根据响应状态码和响应文本进行断言
art_data =[("get","/api/admin/posts",None,200,"OK"),("get","/api/admin/posts",{"page":"1","size":"5"},200,"OK"),("post","/api/admin/posts",{"title":"","Content":"内容","status":"DRAFT","keepRaw":True},400,"字段验证错误,请完善后重试!"),("post","/api/admin/posts",{"title":"test","Content":"content","status":"DRAFT","keepRaw":True},400,"文章别名 test 已存在"),("post","/api/admin/posts",{"title":"文章标题","Content":"文章内容","status":"DRAFT","keepRaw":True},200,"OK"),("put","/api/admin/posts/232/status/RECYCLE",None,404,"Post was not found or has been deleted"),("put","/api/admin/posts/230/status/RECYCLE",None,200,"OK"),("delete","/api/admin/posts",[22],404,"content was not found or has been deleted"),("delete","/api/admin/posts",[485],200,"OK")]# 使用装饰器定义参数名@pytest.mark.parametrize("res_method,url_path,art_body,status_code,msg", art_data)deftest_art(self, res_method, url_path, art_body, status_code, msg):# 参数同步到方法中
res_url = self.base_url + url_path
header ={"Admin-Authorization": self.token}
r_art = self.request(method=res_method, url=res_url, headers=header, json=art_body)assert r_art.json()["message"]== msg # 根据定义的参数进行传参和断言assert r_art.status_code == status_code
if __name__ =='__main__':
pytest.main(["-sv","test_art.py"])
通过读取Json文件实现参数化
使用json文件进行参数化测试,将上文中的art_data参数改为json格式,如下所示
[["get","/api/admin/posts",null,200,"OK"],["get","/api/admin/posts",{"page":"0","size":"1"},200,"OK"],["post","/api/admin/posts",{"title":"","Content":"内容","status":"DRAFT","keepRaw":"True"},400,"字段验证错误,请完善后重试!"],["post","/api/admin/posts",{"title":"test","Content":"content","status":"DRAFT","keepRaw":"True"},400,"文章别名 test 已存在"],["post","/api/admin/posts",{"title":"文章标题","Content":"文章内容","status":"DRAFT","keepRaw":"True"},200,"OK"],["put","/api/admin/posts/232/status/RECYCLE",null,404,"Post was not found or has been deleted"],["put","/api/admin/posts/230/status/RECYCLE",null,200,"OK"],["delete","/api/admin/posts",[22],404,"content was not found or has been deleted"],["delete","/api/admin/posts",[487],200,"OK"]]
然后读取json文件中的数据,进行数据驱动
from com.common import TestCom
import pytest
import json
classTestArt(TestCom):# 打开并读取json文件的中art_data数据
art_data = json.load(open("../data/art_data.json","r", encoding="utf8"))@pytest.mark.parametrize("res_method,url_path,art_body,status_code,msg", art_data)deftest_art(self, res_method, url_path, art_body, status_code, msg):
res_url = self.base_url + url_path
header ={"Admin-Authorization": self.token}
r_art = self.request(method=res_method, url=res_url, headers=header, json=art_body)assert r_art.json()["message"]== msg
assert r_art.status_code == status_code
if __name__ =='__main__':
pytest.main(["-sv","test_art.py"])
通过读取Yaml文件实现参数化
yaml格式相对于json格式的数据更清晰明朗,自己书写需要先了解规则,实际使用哪种格式的数据,请根据实际项目需要和自身对两个数据格式的了解程度决定,这里就直接将上面的json数据转换为yaml格式使用啦,数据如下:
-- get
- /api/admin/posts
-null-200- OK
-- get
- /api/admin/posts
-page:'0'size:'1'-200- OK
…省略了一部分…
然后读取yaml文件中的数据,然后执行用例数据,进而实现参数化测试
from com.common import TestCom
import pytest
import yaml
classTestArt(TestCom):# 跟上文中打开json方式类似,如下所示# art_data = yaml.load(open("../data/art_data.yaml","r",encoding="utf8"),Loader=yaml.FullLoader)# 打开并读取yaml文件的中art_data数据,这次使用with方式打开,这里只是说明还有另一种方式withopen("../data/test_art.yaml","r", encoding="utf8")as f:
art_data = yaml.load(f.read(), Loader=yaml.FullLoader)@pytest.mark.parametrize("res_method,url_path,art_body,status_code,msg", art_data)deftest_art(self, res_method, url_path, art_body, status_code, msg):
res_url = self.base_url + url_path
header ={"Admin-Authorization": self.token}
r_art = self.request(method=res_method, url=res_url, headers=header, json=art_body)assert r_art.json()["message"]== msg
assert r_art.status_code == status_code
if __name__ =='__main__':
pytest.main(["-sv","test_art.py"])
openpyxl库介绍
本次使用openpyxl库,openpyxl是支持读写Excel的python库,支持
xlsx格式
的Excel文件,能够同时读取和修改Excel文档,安装命令
pip install openpyxl
,下面简单介绍一下如何使用,更多信息请查看官方文档
from openpyxl import load_workbook # 导入库
wb = load_workbook("../data/test_data.xlsx")# 打开已存在的excel文件
sheet = wb["登录模块"]# 指定打开名为“登录模块”的工作表
sheet = wb[wb.sheetnames[0]]# 也可以使用下标的方式打开工作表
sheet["A1"]# 选择单元格
sheet.cell(1,1)# 或者使用坐标方式,先行后列
sheet["A1"].value # 获取单元格的值
row = sheet.max_row # 获取总行数
row =len(tuple(sheet.rows))# 或者使用此方式获取总行数
col = sheet.max_column # 获取总列数
col =len(tuple(sheet.columns))# 或者使用此方式获取总列数for x inrange(1,row+1):# 使用for循环打印各单元格中的值for y inrange(1,col+1):# 需要注意的是,不能从0开始,所以要+1,否则拿不到最后一行或一列的值print(sheet.cell(row=x, column=y).value)
sheet["A1"]=22# 给单元格赋值,或者说是修改单元格的值
shell.cell(2,1).value ="用例标题"# 或者使用坐标的方式赋值
wb.save("../data/test_data.xlsx")# 保存文件,对于修改文件操作,必须有保存操作,否者修改不生效
另外需要注意的是对于xlsx格式的Excel文件要使用安装的office工具创建,网上说是office加密处理了,比如将a.txt直接改后缀a.xlsx的方式是不对的,也不能直接在PyCharm之类的工具创建xlsx格式的文件,在打开时会报文件扩展名无效之类的错误,总之,最简单的方法还是在电脑上通过右键创建Excel文件,然后放到所需位置,当然网上也有其它办法,这里就不细说啦!
通过读取Excel文件实现参数化
首先将测试用例写入Excel文件中,如下图所示
使用openpyxl读取Excel文件中的信息,完成参数化测试,如下所示:
from com.common import TestCom
import pytest
import json
from openpyxl import load_workbook
classTestArt(TestCom):
wb = load_workbook("../data/test_art.xlsx")# 打开Excel文件
sheet = wb[wb.sheetnames[0]]# 打开第一个工作表
total_rows = sheet.max_row # 获取总行数
art_data =[]# 新建一个空列表,将读取出来的每行数据存放到列表中for x inrange(2, total_rows +1):
case_data =[]# 组装每行列表数据,形成一个列表集合for y inrange(3,8):# 获取第3列到第7列的数据
case_data.append(sheet.cell(row=x, column=y).value)# 将每行单元格数据添加到case_data列表中
art_data.append(case_data)# 将每行数据添加到art_data列表中@pytest.mark.parametrize("res_method,url_path,art_body,status_code,msg", art_data)deftest_art(self, res_method, url_path, art_body, status_code, msg):
res_url = self.base_url + url_path
header ={"Admin-Authorization": self.token}
r_art = self.request(method=res_method, url=res_url, headers=header, json=json.loads(art_body))assert r_art.json()["message"]== msg
assert r_art.status_code == status_code
if __name__ =='__main__':
pytest.main(["-sv","test_art.py"])
封装文件解析方法
新建
file_tools.py
文件,封装json文件、yaml文件和Excel文件的解析方法
import json
import yaml
from openpyxl import load_workbook
classFileTools():defjson_file(self,filename):# 打开json文件并读取数据
art_data = json.load(open(filename,"r", encoding="utf8"))return art_data
defyaml_file(self,filename):# 打开yaml文件并读取数据withopen(filename,"r", encoding="utf8")as f:
art_data = yaml.load(f.read(), Loader=yaml.FullLoader)return art_data
defexcel_file(self,filename,sheetname):# 打开Excel文件并读取数据
wb = load_workbook(filename)# 打开指定Excel文件
sheet = wb[sheetname]# 打开指定工作表
total_rows = sheet.max_row # 获取总行数
art_data =[]# 新建一个空列表,将读取出来的每行数据存放到列表中for x inrange(2, total_rows +1):# 读取每行数据
case_data =[]# 组装每行列表数据,形成一个列表集合for y inrange(3,8):# 获取第3列到第7列的数据
case_data.append(sheet.cell(row=x, column=y).value)# 将每行单元格数据添加到case_data列表中
art_data.append(case_data)# 将每行数据添加到art_data列表中return art_data
以读取Excel文件为例,调用封装后的方法即可
from com.common import TestCom
import pytest
import json
from com.file_tools import FileTools # 导入封装FileTools类classTestArt(TestCom):# 调用封装的方法
art_data = FileTools().excel_file("../data/art_data.xlsx","Sheet1")@pytest.mark.parametrize("res_method,url_path,art_body,status_code,msg", art_data)deftest_art(self, res_method, url_path, art_body, status_code, msg):
res_url = self.base_url + url_path
header ={"Admin-Authorization": self.token}
r_art = self.request(method=res_method, url=res_url, headers=header, json=json.loads(art_body))assert r_art.json()["message"]== msg
assert r_art.status_code == status_code
if __name__ =='__main__':
pytest.main(["-sv","test_art.py"])
以上就完成啦!使用Excel文件实现参数化相对较复杂一点,而且上面的示例也没有将测试结果写入到Excel文件中,只是读取了Excel中的用例,所以既然使用Excel文件就把结果记录到文件中吧,考虑到Excel中可能记录了各种场景的测试用例,若只想执行某几条,又不想增删Excel中其它数据,可以加个判断,只执行做了标记的用例,方法如下:
首先在Excel文件中增加用例ID、是否执行、和测试结果字段,先根据是否执行判断哪些用例需要执行,然后将结果写入对应的测试结果中,Excel如下图所示
重新封装Excel解析方法
封装读取Excel文件和写入Excel文件的方法,代码如下:
classFileTools():# 封装读取Excel文件defread_excel(self,filename,sheetname):
wb = load_workbook(filename)# 打开指定的Excel文件
sheet = wb[sheetname]# 打开指定的工作表
total_rows = sheet.max_row # 获取总行数
art_data =[]# 新建一个空列表,将读取出来的每行数据存放到列表中for x inrange(2, total_rows +1):# 循环读取每行数据
case_data =[]# 组装每行列表数据,形成一个列表集合for y inrange(3,10):# 获取第3列到第9列的数据
case_data.append(sheet.cell(row=x, column=y).value)# 将每行单元格数据添加到case_data列表中
art_data.append(case_data)# 将每行数据添加到art_data列表中return art_data # 返回art_data# 封装写入Excel文件defwrite_excel(self,filename,id,result):
wb = load_workbook(filename)# 打开指定的Excel文件
sheet = wb[wb.sheetnames[0]]# 打开第一个工作表
sheet.cell(id+1,10).value = result # 将测试结果写入Excel文件中
wb.save(filename)# 保存修改后的Excel文件
调用封装的方法进行测试,注意:因为有写入操作,执行时Excel文件必须处于关闭状态,否则报权限被拒
from com.common import TestCom
import pytest
import json
from com.file_tools import FileTools
classTestArt(TestCom):# 读取Excel文件中的数据
art_data = FileTools().read_excel("../data/art_data.xlsx","Sheet1")@pytest.mark.parametrize("case_id,res_method,url_path,art_body,status_code,msg,is_run", art_data)deftest_art(self, case_id, res_method, url_path, art_body, status_code, msg, is_run):if is_run =="是":# 只执行标记“是”的用例
res_url = self.base_url + url_path
header ={"Admin-Authorization": self.token}
r_art = self.request(method=res_method, url=res_url, headers=header, json=json.loads(art_body))if r_art.status_code == status_code and r_art.json()["message"]== msg:# 使用if…else做断言
real_result ="Pass"else:
real_result ="Fail"# 保存填写测试结果后的Excel文件
FileTools().write_excel("../data/art_data.xlsx", case_id, real_result)if __name__ =='__main__':
pytest.main(["-sv","test_art.py"])
测试报告
Pytest-html报告
可以使用pytest-html生成报告,先执行命令
pip install pytest_html
安装,运行测试用例时加上
--html = report.html
参数即可
from com.common import TestCom
import pytest
from com.file_tools import FileTools
classTestArt(TestCom):
art_data = FileTools().json_file("../data/art_data.json")@pytest.mark.parametrize("res_method,url_path,art_body,status_code,msg", art_data)deftest_art(self, res_method, url_path, art_body, status_code, msg,):
res_url = self.base_url + url_path
header ={"Admin-Authorization": self.token}
r_art = self.request(method=res_method, url=res_url, headers=header, json=art_body)assert r_art.json()["message"]== msg
assert r_art.status_code == status_code
if __name__ =='__main__':
pytest.main(["-sv","test_art.py","--html=../report/art_report.html"])# 执行用例并生成报告到report目录下
报告如下所示
Allure报告
当然也可以使用更强大的Allure报告,关于Allure安装使用请查看此文章,这里只做简单演示
from com.common import TestCom
import pytest, os
from com.file_tools import FileTools
classTestArt(TestCom):
art_data = FileTools().yaml_file("../data/art_data.yaml")@pytest.mark.parametrize("res_method,url_path,art_body,status_code,msg", art_data)deftest_art(self, res_method, url_path, art_body, status_code, msg,):
res_url = self.base_url + url_path
header ={"Admin-Authorization": self.token}
r_art = self.request(method=res_method, url=res_url, headers=header, json=art_body)assert r_art.json()["message"]== msg
assert r_art.status_code == status_code
if __name__ =='__main__':# 执行用例并生成Allure报告
pytest.main(["-sv","test_art.py","--alluredir=../report/allure-results","--clean-alluredir"])
os.system("allure generate ../report/allure-results -o ../report/allure-report")
补充:文件上传
忘记写上传文件的接口了,这里补充一下吧,其实跟读取文件类似,首先在封装的request中添加
files=None
,或者直接使用requests库,如下所示,使用的是自己封装的request函数,注意:上传文件需要使用读取二进制的方式,用
rb
表示,注意文件类型,不同文件的
Content-Type
肯定也是不同的
from com.common import TestCom
import pytest
from com.file_tools import FileTools
classTestArt(TestCom):deftest_art(self):
res_url = self.base_url +"/api/admin/attachments/upload"
file_data ={"file":("这里指上传后的图片名称.jpg",open("../data/picture.jpg","rb"),"image/jpeg")}
header ={"Admin-Authorization": self.token}
r_art = self.request(method="post", url=res_url, headers=header, files=file_data)assert r_art.json()["message"]=="OK"assert r_art.status_code ==200if __name__ =='__main__':
pytest.main(["-sv","test_art.py"])
同样可以封装上传文件的解析方法后再调用,在
file_tools.py
中添加以下信息
defupload_file(self,filename,filepath,filetype):
file_data ={"file":(filename,open(filepath,"rb"),filetype)}return file_data
封装后就可以调用解析方法啦,同时可以像测试上面介绍的其它接口一样,也可以实现参数化,如下所示
from com.common import TestCom
import pytest
from com.file_tools import FileTools
classTestArt(TestCom):# 参数化数据
file_data =[("上传jpg格式的图片.jpg","../data/picture.jpg","image/jpeg",200,"OK"),("上传png格式的图片.png","../data/icon.png","image/png",200,"OK"),("上传txt格式的文档.txt","../data/新建文本文档.txt","text/plain",200,"OK"),("上传json格式的文件.json","../data/art_data.json","application/json",200,"OK"),("上传yaml格式的文件.yaml","../data/art_data.yaml","application/octet-stream",200,"OK"),("上传cer格式的证书.cer","../data/Root.cer","application/x-x509-ca-cert",200,"OK"),("上传exe格式的程序.exe","../data/geek.exe","application/x-msdownload",200,"OK"),("上传xlsx格式的文档.xlsx","../data/art_data.xlsx","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",200,"OK")]@pytest.mark.parametrize("filename,filepath,filetype,status_code,msg", file_data)deftest_art(self, filename, filepath, filetype, status_code, msg):
res_url = self.base_url +"/api/admin/attachments/upload"
file_data = FileTools().upload_file(filename, filepath, filetype)# 调用封装的文件解析函数
header ={"Admin-Authorization": self.token}
r_art = self.request(method="post", url=res_url, headers=header, files=file_data)assert r_art.json()["message"]== msg
assert r_art.status_code == status_code
if __name__ =='__main__':
pytest.main(["-sv","test_art.py"])
至此,Pytest+Requests+Logging+报告的自动化框架基本就完成了,更多功能及其实现方法还请自行探索吧!
版权归原作者 小白典 所有, 如有侵权,请联系我们删除。