【接口自动化】Python+Requests接口自动化测试框架搭建【三】

时间:2021-08-25 18:05:29

经过上两篇文章的讲解,我们已经完成接口自动化的基础框架,现在开始根据实际项目丰满起来。

在PyCharm中新建项目,项目工程结构如下:

【接口自动化】Python+Requests接口自动化测试框架搭建【三】

config:配置文件夹,可以将一些全局变量放于配置文件中,方便调用。例如:URL地址、端口,开发如经常更换端口在配置文件中便可修改。

【接口自动化】Python+Requests接口自动化测试框架搭建【三】

data:测试用例表格文件夹,存放测试用例.xls和读取写入excel表的文件。

demo:程序公共类文件夹,存放程序公共类,例如:POST/GET请求,获取Token等。

kit:相关测试工具,压缩、发送邮件。

main:程序运行主文件夹。

report:测试报告文件夹。

test_case:测试用例。

公司实际开发项目中,存在接口依赖,即Token。Token获取方法为登录后返回Token值,所以我们编写一个登录类获取Token,并将Token放置于config文件中,成为全局变量,等其他接口调用时,直接引用变量。

在demo中新建DemoMain请求方法公共类。

DemoMain.py

#!/usr/bin/env python
# encoding: utf-8
'''
@author: GongYu
@license: (C) Copyright 2020
@contact: gong_yu_1996@163.com
@software: PyCharm
@file: DemoMain.py
@time: 2020/7/31 10:58
@desc: 公共方法主类
''' import requests class RunMain:
def __init__(self):
pass @staticmethod
def send_post(url,cookies,headers,params=None):
try:
res = requests.post(url=url,cookies=cookies,headers=headers,data=params)
return res.status_code # 返回响应状态代码
except Exception as msg:
return msg @staticmethod
def send_get(url,cookies,headers,params=None,):
try:
res = requests.get(url=url,cookies=cookies,headers=headers,params=params,)
return res.status_code # 返回响应状态代码
except Exception as msg:
return msg def run_main(self, url,method,cookies=None,headers=None,params=None):
if method == 'GET':
res = self.send_get(url,cookies,headers,params)
return res
elif method == 'POST':
res = self.send_post(url,cookies,headers,params)
return res
else:
print('不支持的请求方式!')

CaseDemo.py 根据接口分开编写请求方法

【接口自动化】Python+Requests接口自动化测试框架搭建【三】

实际开发中遇到的问题(仅为自己记录):

1.return返回值的问题:实际项目测试中我们不能只返回code状态码,因为接口测试中不能只凭返回状态码进行断言,我们测试判定中还应检查接口返回值,接口返回值应在开发提供接口文档已经注明。所以上述代码中我们应将返回值改为res。

2.因为一开始只考虑断言状态码的问题,所以导致多新建一个LoginDemo请求类,如返回值为res,此文件即可删除。

3.配置文件中可以参考Postman中多种参数的方式进行配置,可根据实际项目自己增加,极其方便。还可以配置数据库等信息,不再赘述。

data文件夹:

【接口自动化】Python+Requests接口自动化测试框架搭建【三】

GetExcel.py:获取测试用例操作类

#!/usr/bin/env python
# encoding: utf-8
'''
@author: GongYu
@license: (C) Copyright 2020
@contact: gong_yu_1996@163.com
@software: PyCharm
@file: GetExcel.py
@time: 2020/8/12 17:03
@desc:获取Excel表格数据
'''
import xlrd
def get_excel_data(sheetName,idCol=0,bodyCol=7,repsCol=9): # 获取数据函数
dataList = [] # 生成一个空字典
workSheet = xlrd.open_workbook('../data/JDData.xls', formatting_info=True).sheet_by_name(sheetName) # 获取表格
endRow = workSheet.nrows # 获取有效工作行数
for one in range(1, endRow): # 循环获取
if workSheet.cell(one,12).value == 'Yes': # 判断用例是否执行
dataList.append((workSheet.cell(one, idCol).value,workSheet.cell(one, bodyCol).value, workSheet.cell(one, repsCol).value)) # 追加dataList
else:
pass
return dataList

固定读取表格中的数据即:第1列,第8列,第10列数据,所以形参写死。

SetExcel.py:测试结果写入Excel表格

#!/usr/bin/env python
# encoding: utf-8
'''
@author: GongYu
@license: (C) Copyright 2020
@contact: gong_yu_1996@163.com
@software: PyCharm
@file: SetExcel.py
@time: 2020/8/14 17:01
@desc: 写入Excel
'''
import xlrd
import json
from xlutils import copy
class CSetExcel():
def SetExcel(self,sheetName,infodata,one,Col=10):
path = '../data/JDData.xls'
workSheet = xlrd.open_workbook(path,formatting_info=True)
New_workSheet = copy.copy(workSheet) # 复制表格
New_Sheet = New_workSheet.get_sheet(sheetName) # 获取表格名
New_Sheet.write(one,Col,json.dumps(infodata,ensure_ascii=False)) # 写入Excel
New_workSheet.save(path) # 保存

此处坑:

1.New_Sheet.write(one,Col,json.dumps(infodata,ensure_ascii=False)) # 写入Excel

如不加ensure_ascii=False,写入Excel中中文会被转义unicode编码。json.dumps(infodata):接口返回值为字典类型(dict)转换成字符串类型写入excel。

2.执行方法前,记得关闭Excel表格。

Data.excel模板格式

【接口自动化】Python+Requests接口自动化测试框架搭建【三】

此处坑:(MD,我也不知道我为啥这么多坑踩.........心累啊,前人种树后人乘凉,我先填为敬。)

编号一列单元格格式为文本,不要设置成数值,设置成数值会被Python取出后变为1.0(浮点型),然后导致的结果就是SetExcel.py根据编号写入数据时,无法识别1.0单元格在哪里,出现错误。

kit文件夹:存放测试报告压缩工具类以及发送邮件类

【接口自动化】Python+Requests接口自动化测试框架搭建【三】

ZipTool.py 压缩文件工具类

#!/usr/bin/env python
# encoding: utf-8
'''
@author: GongYu
@license: (C) Copyright 2020
@contact: gong_yu_1996@163.com
@software: PyCharm
@file: ZipTool.py
@time: 2020/8/19 9:52
@desc: 压缩文件夹工具
'''
import os
import zipfile
import shutil
class FileZip():
def File(self,source_dir, output_filename):# 打包目录为zip文件(未压缩)
if os.path.exists('../report/Report.zip') == True : # 判断文件是否存在
print("报告压缩文件存在!正在执行删除........")
os.remove('../report/Report.zip') # 删除文件
print("删除成功!正在执行打包操作........")
zipf = zipfile.ZipFile(output_filename, 'w')
pre_len = len(os.path.dirname(source_dir))
for parent, dirnames, filenames in os.walk(source_dir):
for filename in filenames:
pathfile = os.path.join(parent, filename)
arcname = pathfile[pre_len:].strip(os.path.sep) # 相对路径
zipf.write(pathfile, arcname)
zipf.close()
shutil.move('../main/Report.zip', '../report') # 移动文件
print('打包完成,正在发送报告......')
else:
print('正在执行打包操作......')
zipf = zipfile.ZipFile(output_filename, 'w')
pre_len = len(os.path.dirname(source_dir))
for parent, dirnames, filenames in os.walk(source_dir):
for filename in filenames:
pathfile = os.path.join(parent, filename)
arcname = pathfile[pre_len:].strip(os.path.sep)
zipf.write(pathfile, arcname)
zipf.close()
shutil.move('../main/Report.zip', '../report')
print('打包完成,正在发送报告......')

EmailTool.py 发送邮件工具类

#!/usr/bin/env python
# encoding: utf-8
'''
@author: GongYu
@license: (C) Copyright 2020
@contact: gong_yu_1996@163.com
@software: PyCharm
@file: EmailTool.py
@time: 2020/8/19 9:53
@desc: 发送邮件工具
''' import smtplib
import time
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
class SendEmail():
def Email(slef):
# 第三方 SMTP 服务
mail_host = "smtp.163.com" # 设置服务器
mail_user = "gong_yu_1996@163.com" # 用户名
mail_pass = " " # 口令 sender = 'gong_yu_1996@163.com'
receivers = ['gong_yu_1996@163.com','1196936134@qq.com'] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱,多邮箱设置为数组 message = MIMEMultipart()
message['From'] = Header("GongYu", 'utf-8')
#message['To'] = Header("测试", 'utf-8')
ReportTime = time.strftime("%Y-%m-%d %H:%M")
subject = '['+ReportTime +']接口自动化测试报告'
message['Subject'] = Header(subject, 'utf-8') # 邮件正文内容
NewTime = time.strftime("%Y-%m-%d %H:%M")
message.attach(MIMEText('各位领导,同事:附件内容为接口自动化测试报告。测试时间为:'+NewTime+',请下载查看!', 'plain', 'utf-8'))
# 附件
att1 = MIMEText(open('../data/Data.xls', 'rb').read(), 'base64', 'utf-8')
att1["Content-Type"] = 'application/octet-stream'
att1["Content-Disposition"] = 'attachment; filename="JDData.xls"'
message.attach(att1) att2 = MIMEText(open('../report/Report.zip', 'rb').read(), 'base64', 'utf-8')
att2["Content-Type"] = 'application/octet-stream'
att2["Content-Disposition"] = 'attachment; filename="Report.zip"'
message.attach(att2) try:
smtpObj = smtplib.SMTP()
smtpObj.connect(mail_host, 25) # 25 为 SMTP 端口号
smtpObj.login(mail_user, mail_pass)
smtpObj.sendmail(sender, receivers, message.as_string())
print("邮件发送成功!")
except smtplib.SMTPException:
print("Error: 无法发送邮件")

下面我们编写Test_case.py 文件,放置于test_case文件夹下

Test_case.py

#!/usr/bin/env python
# encoding: utf-8
'''
@author: GongYu
@license: (C) Copyright 2020
@contact: gong_yu_1996@163.com
@software: PyCharm
@file: Test_test.py
@time: 2020/8/13 9:25
@desc: 测试执行
'''
import json
import pytest
import allure
from data.GetExcel import get_excel_data
from data.SetExcel import CSetExcel
from demo.CaseDemo import addapi @allure.feature('地址接口测试')
class Testaddapi(): @allure.story('获取一级地址')
@pytest.mark.parametrize('idData,infoData,resultdata',get_excel_data('1-获取一级地址'))
def test_getProvince(self,idData,infoData,resultdata):
Res = addapi().getProvince()
CSetExcel().SetExcel('1-获取一级地址', Res.json(), json.loads(idData))
assert Res.status_code == 200
assert Res.json()['resultMessage'] == json.loads(resultdata)['resultMessage']
assert Res.json()['success'] == json.loads(resultdata)['success']

我们采用的是Pytest测试框架,使用allure输出测试报告。

到这里我们的测试框架就已经完成了,下面我们编写一个运行主类:TestMain.py 进行启动测试。

#!/usr/bin/env python
# encoding: utf-8
'''
@author: GongYu
@license: (C) Copyright 2020
@contact: gong_yu_1996@163.com
@software: PyCharm
@file: TestMain.py
@time: 2020/8/13 10:32
@desc: 运行主类
'''
import shutil
import pytest
import os
from kit.ZipTool import FileZip
from kit.EmailTool import SendEmail if __name__ == '__main__': # 启动allure
if os.path.exists('../report/xml') == True or os.path.exists('../report/html') == True:
print("报告目录存在!正在执行删除........")
shutil.rmtree(r'../report/xml')
shutil.rmtree(r'../report/html')
print("删除成功!正在执行测试用例........")
pytest.main(['../test_case/Test_test.py', '-s', '--alluredir', '../report/xml']) # 生成测试报告文件
print('测试报告转换HTML.....')
os.system('allure generate ../report/xml -o ../report/html') # 将测试报告文件转化为HTML
FileZip().File('../report', 'Report.zip')
SendEmail().Email()
os.system('allure serve ../report/xml') # 运行测试报告文件
else:
print('正在执行测试用例........')
pytest.main(['../test_case/Test_test.py', '-s', '--alluredir', '../report/xml'])
print('测试报告转换HTML.....')
os.system('allure generate ../report/xml -o ../report/html')
FileZip().File('../report', 'Report.zip')
SendEmail().Email()
os.system('allure serve ../report/xml')

下面我们运行试一下:

控制台输出:

【接口自动化】Python+Requests接口自动化测试框架搭建【三】

Allure测试报告:【接口自动化】Python+Requests接口自动化测试框架搭建【三】

下面我们登录一下邮箱查看一下:

【接口自动化】Python+Requests接口自动化测试框架搭建【三】

【接口自动化】Python+Requests接口自动化测试框架搭建【三】

我们已经将测试结果加入附件发送到了负责人的邮箱,到此我们的自动化测试框架已经完成。最后还有一个日志功能还没实现,等找个时间再来补充吧。

新手上路,知识不足,如有纰漏错误,还请大神们指正,谢谢各位咯~~~