搭建一个自动化测试的框架。
环境Python3.8.5+pycharm社区版
OK,看一下框架分层与封装。
utils:封装了读取测试数据方法,重写打印日志,断言方法,封装requests请求方法,封装webdriver请求方法,发送邮件方法,没写数据库连接池,后面慢慢完善。
logs:运行日志保存位置
test_case:测试用例函数文件
test_data:测试数据(参数分离)
test_reoprt:测试报告输出
config.py中指定表格中数据的X,Y轴坐标及接口url前缀
runner.py运行
各个文件说明:
先来看一下excel中的数据:
config.py如下:
# config.py
import os
# 基路径
base_path = os.path.dirname(__file__)
# 主机地址
host = "http://127.0.0.1:5000"
# excel数据对应列
cell_config = {
"path": 5,
"method": 6,
"params_type": 7,
"headers": 8,
"params": 9,
"expect": 10,
"is_run": 11,
"result": 12,
"desc": 13
}
file_tools中通过读取excel中指定表格数据,并写入json文件中。
在测试用例中可使用read_json()配合@pytest.mark.parametrize()装饰器将测试数据传入
# utils/file_tools
import json
import os
import openpyxl
from config import base_path, cell_config
def read_json(filename):
filename = base_path + os.sep + "test_data" + os.sep + filename
with open(filename, 'r', encoding='utf8') as f:
return json.load(f)
def write_json(case, filename):
filename = base_path + os.sep + "test_data" + os.sep + filename
with open(filename, "w", encoding='utf8') as f:
# 加上4个空格;False 不转义
json.dump(case, f, indent=4, ensure_ascii=False)
class FileTool:
def __init__(self, filename):
self.filename = base_path + os.sep + "test_data" + os.sep + filename # 动态文件路径
print("要打开的文件为", self.filename)
self.workbook = openpyxl.load_workbook(self.filename) # 打开文件,获取workbook对象
self.sheet = self.workbook[self.workbook.sheetnames[0]] # 获取sheet表单对象
self.row = self.sheet.max_row # 获取总行数
print("总行数为", self.row)
def read_excel(self):
# 新建一个空列表(存储每行的数据)
case = list()
# 遍历每行数据
for i in range(2, self.row + 1):
# 新建空字典(存储每行数据)
data = dict()
# 判断是否执行
if self.sheet.cell(i, cell_config.get("is_run")).value == "是":
try:
# 读取数据追加到字典
data['path'] = self.sheet.cell(i, cell_config.get("path")).value
data['method'] = str(self.sheet.cell(i, cell_config.get("method")).value)
data['params_type'] = self.sheet.cell(i, cell_config.get("params_type")).value
data['headers'] = json.loads(self.sheet.cell(i, cell_config.get("headers")).value)
data['params'] = eval(self.sheet.cell(i, cell_config.get("params")).value)
# 此处预期结果用str(), json.loads()或者eval() 处理,将json格式转为str,以实际情况选择对应函数
data['expect'] = str(self.sheet.cell(i, cell_config.get("expect")).value)
# 记录每行数据的行与列,执行完写入结果使用
data['x_y'] = [i, cell_config.get("result")]
# 将字典追加到列表
case.append(data)
# 读取结果写入excel
self.write_excel([i, cell_config.get("desc")], "数据读取完成!~")
except Exception as e:
self.write_excel([i, cell_config.get("desc")], e)
print(data)
# 将列表数据写入json
write_json(case, "case.json")
print("读取-的数据为", case)
def write_excel(self, x_y, msg):
try:
# x_y 参数为列表,如:[2,5] 2行5列
self.sheet.cell(x_y[0], x_y[1]).value = msg
except Exception as e:
self.sheet.cell(x_y[0], x_y[1]).value = e
finally:
# 保存excel
self.workbook.save(self.filename)
断言:不同项目的接口返回值进行断言,json断言、文本断言、状态码断言等。
# /utils/my_assert.py
def common_assert_ture(response, case):
# 获取响应数据
result = response.json()
# 获取预期数据
expect = case.get("expect")
print(str(expect))
print(str(result.get('code')))
assert str(result.get('code')) in str(expect)
requests.py简单封装一下:
# /utils/my_requests.py
import requests
from config import host
from utils.logger import logger
class Api:
def __init__(self, case):
# 提取 url = host+path
self.url = host + case.get("path")
# 请求方法
self.method = case.get("method")
# 请求参数类型
self.params_type = case.get("params_type")
# 请求信息头
self.headers = case.get("headers")
# 请求参数
self.params = case.get("params")
def __get(self):
logger.info("正在调用get请求方法")
return requests.get(url=self.url, json=self.params, headers=self.headers)
def __post(self):
logger.info("正在调用post请求方法")
if self.params_type == 'json':
return requests.post(url=self.url, json=self.params, headers=self.headers)
elif self.params_type == 'data':
return requests.post(url=self.url, data=self.params, headers=self.headers)
def __put(self):
logger.info("正在调用put请求方法")
return requests.put(url=self.url, json=self.params, headers=self.headers)
def __delete(self):
logger.info("正在调用delete请求方法")
return requests.delete(url=self.url, json=self.params, headers=self.headers)
def run_method(self):
if self.method.upper() == "POST":
return self.__post()
if self.method.upper() == 'GET':
return self.__get()
if self.method == 'put':
return self.__put()
if self.method == 'delete':
return self.__delete()
my_webdriver.py这个就不上代码了,量有点多,截图。
qq_email.py不往上放了,主要就是借助 smtplib.SMTP('smtp.qq.com', 25) 的QQ邮箱发送测试报告附带HTML。
来看一下test_001.py测试用例:
import pytest
from jsonpath import jsonpath
from utils.file_tools import FileTool
from utils.file_tools import read_json
from utils.logger import logger
from utils.my_assert import common_assert_ture
from utils.my_requests import Api
class Test001:
tool = FileTool("test_001.xlsx") # 实例化获取工具类对象
tool.read_excel() # 读取excel -> 将数据从excel中读取并写入json文件中
@pytest.mark.parametrize("case", read_json("case.json"))
def test_001(self, case):
logger.info(f'接口地址:{case.get("path")}')
r01 = Api(case).run_method() # 调用执行接口方法
logger.info(f'响应结果:{r01.json()}')
print("r是这个东西:", r01)
common_assert_ture(r01, case) # 断言
Test001.tool.write_excel(case.get("x_y"), "执行成功!~") # 执行结果写入excel
r01 = jsonpath(r01.json(), "$.code")[0] # 从返回值中提取需要的参数
print(r01)
if __name__ == '__main__':
pytest.main()
最后pytest.ini中我重点设定了HTML报告路径,没有指定测试数据这些东西,需要可以后面慢慢加。
[pytest]
addopts =-q --html=../test_report/report.html --self-contained-html --cache-clear
运行:
runner.py
import pytest
if __name__ == '__main__':
pytest.main(['--html=../test_report/report.html'])
至此框架基本成型,基础功能已具备。连接数据库考虑使用连接池。慢慢学。