测试报告阉割掉pytest-html,只支持allure生成测试报告

This commit is contained in:
floraachy
2023-08-02 11:25:54 +08:00
parent bc25d5156b
commit 0fbd9eb5c8
13 changed files with 102 additions and 465 deletions

View File

@@ -7,7 +7,6 @@ name = "pip_conf_index_global"
pymysql = "*"
loguru = "*"
requests-toolbelt = "*"
beautifulsoup4 = "*"
requests = "*"
openpyxl = "*"
sshtunnel = "*"
@@ -17,7 +16,6 @@ click = "*"
faker = "*"
jsonpath = "*"
pytest = "==6.2.5"
pytest-html = "==2.1.1"
pytest-rerunfailures = "*"
allure-pytest = "==2.9.45"
pydantic = "*"

View File

@@ -5,13 +5,13 @@
那么本自动化框架,将为你解决这些问题。
- 框架主要使用 python 语言编写,结合 pytest 进行二次开发,用户仅需要在 yaml 或者 excel 文件中编写测试用例, 编写成功之后,会自动生成测试用例代码,零基础代码小白,也可以操作。
- 如果是具备代码基础的,也可以直接通过 py 文件编写测试用例。
- 使用 pytest-html / Allure 生成报告,并针对测试报告样式进行了调整,使得报告更加美观;
- 使用 Allure 生成报告,并针对测试报告样式进行了调整,使得报告更加美观;
- 测试完成后,支持发送 企业微信通知/ 钉钉通知/ 邮箱通知,灵活配置。
## 一、框架介绍
本框架主要是基于 Python + pytest + pytest-html/Allure + loguru + 邮件通知/企业微信通知/钉钉通知 实现的接口自动化框架。
本框架主要是基于 Python + pytest + Allure + loguru + 邮件通知/企业微信通知/钉钉通知 实现的接口自动化框架。
* git地址: [https://www.gitlink.org.cn/floraachy/apiautotest](https://www.gitlink.org.cn/floraachy/apiautotest)
* 项目参与者: floraachy
@@ -34,7 +34,7 @@
* 支持利用allure设置用例优先级运行指定优先级的用例
* 测试数据隔离, 实现数据驱动
* 自动生成用例代码: 测试人员在yaml/excel文件中填写好测试用例, 程序可以直接生成用例代码,纯小白也能使用
* 多种报告随心选择框架支持pytest-html以及Allure测试报告可以动态配置所需报告
* 使用Allure生成测试报告,并对测试报告进行了定制化修改,使得测试报告更加美观
* 日志模块: 采用loguru管理日志可以输出更为优雅简洁的日志
* 钉钉、企业微信通知: 支持多种通知场景,执行成功之后,可选择发送钉钉、或者企业微信、邮箱通知
* 执行环境一键切换,解决多环境相互影响问题
@@ -52,14 +52,13 @@
│ ├────case_fun_handle.py 根据配置文件,从指定类型文件中读取用例数据,并调用生成用例文件方法,生成用例文件
│ ├────data_handle.py 数据处理
│ ├────extract_data_handle.py 提取数据的一些方法
│ ├────get_results_handle.py 从pytest-html/allure测试报告中获取测试结果
│ ├────get_results_handle.py 从allure测试报告中获取测试结果
│ ├────platform_handle.py 跨平台的支持allure用于生成allure测试报告
│ ├────request_data_handle.py 针对用例数据进行请求前后的处理
│ └────send_result_handle.py 根据配置文件,从html测试报告中获取测试结果发送指定类型的通知
│ └────send_result_handle.py 根据配置文件,发送指定类型的测试结果
├────common_utils/ 公共的工具类
│ ├────__init__.py
│ ├────base_request.py 封装的requests请求
│ ├────bs4_handle.py bs4BeautifulSoup4是Python中的第三方库。可以从HTML或XML文件中提取数据的Python库。
│ ├────dingding_handle.py 封装的钉钉机器人
│ ├────excel_handle.py 处理excel
│ ├────files_handle.py 处理文件相关操作
@@ -77,8 +76,6 @@
│ │ ├────http_server.exe http服务用来放置在allure压缩包中方便在不安装allure环境下打开allure报告
│ │ ├────logo.svg 保存用来替换allure报告的logo的在代码中无用处
│ │ ├────双击打开Allure报告.bat .bat文件用来放置在allure压缩包中方便在不安装allure环境下打开allure报告
│ ├────pytest_html_config/
│ │ ├────pytest_html_report.css 改变pytest-html测试报告的样式文件
│ ├────case_template.txt 自动生成的测试用例文件模板
│ ├────global_vars.py 保存的一些全局变量
│ ├────path_config.py 项目路径管理
@@ -113,7 +110,6 @@
pymysql = "*"
loguru = "*"
requests-toolbelt = "*"
beautifulsoup4 = "*"
requests = "*"
openpyxl = "*"
sshtunnel = "*"
@@ -123,7 +119,6 @@ click = "*"
faker = "*"
jsonpath = "*"
pytest = "==6.2.5"
pytest-html = "==2.1.1"
pytest-rerunfailures = "*"
allure-pytest = "==2.9.45"
pydantic = "*"
@@ -251,11 +246,8 @@ excel表单2名称是示例模块
- 如果pycharm.interpreter拥有了框架所需的所有依赖包可以通过pycharm直接在`run.py`中右键运行
## 八、查看测试报告
### pytest-html测试报告
如果是pytest-html生成的测试报告直接打开`outputs`目录下的`.html`报告即可。支持通过任意浏览器打开查看
### Allure测试报告
1. 如果是Allure生成的测试报告支持通过pycharm点击`outputs/report/allure_html/index.html`打开查看测试报告
1. Allure生成的测试报告支持通过pycharm点击`outputs/report/allure_html/index.html`打开查看测试报告
2. 如果不通过pycharm打开直接通过文件夹打开windows系统环境下可以点击`outputs/report/allure_html/双击打开Allure报告.bat`打开查看测试报告
注意:

View File

@@ -5,86 +5,15 @@
# @Software: PyCharm
# @Desc: 从测试报告中获取测试结果
# 标准库导入
import os
from loguru import logger
import json
from common_utils.bs4_handle import SoupAPI
# 第三方库导入
from loguru import logger
# 本地应用/模块导入
from common_utils.time_handle import timestamp_strftime
def get_test_results_from_pytest_html_report(html_report_path):
"""
通过BeautifulSoup4解析pytest-html生成的html报告获取测试结果及测试情况
:param html_report_path: pytest-html测试报告的绝对路径
"""
try:
bs = SoupAPI(html_report_path)
test_results = {}
# -------------- 获取测试环境信息 --------------
environment_info = bs.get_element_by_id("environment").get_text()
new_environment_info = [element for element in environment_info.split("\n") if element != '']
info_mapping = {
"Platform": "platform",
"Python": "python_version",
"开始时间": "start_time",
"项目名称": "project_name",
"项目环境": "run_env"
}
for key, value in enumerate(new_environment_info):
if value in info_mapping:
test_results[info_mapping[value]] = new_environment_info[key + 1]
# -------------- 获取测试人员,所属部门,测试用例总数,运行时长 --------------
p_elements = bs.get_elements_by_tag("p")
for p_element in p_elements:
res = p_element.get_text()
if "测试人员:" in res:
test_results['tester'] = res.replace("测试人员:", "")
if "所属部门: " in res:
test_results['department'] = res.replace("所属部门: ", "")
if "tests ran" in res:
new_list = res.split(" ")
test_results['run_time'] = f"{new_list[4]}"
# -------------- 获取具体结果 --------------
result_mapping = {
'passed': 'span.passed', # 通过的用例个数
'skipped': 'span.skipped', # 跳过的用例个数
'failed': 'span.failed', # 失败的用例个数
'broken': 'span.error', # 错误的用例个数
'xfailed': 'span.xfailed', # 预期失败的用例个数
'xpassed': 'span.xpassed', # 意外通过的用例个数
'rerun': 'span.rerun' # 重跑的用例个数
}
for key, value in result_mapping.items():
result_element = bs.select_element(value)[0]
test_results[key] = int(result_element.get_text().split(" ")[0])
# 用例总数
test_results['total'] = test_results["passed"] + test_results["skipped"] + test_results["failed"] + \
test_results["broken"] + test_results["xfailed"] + test_results["xpassed"]
# 通过率
# 判断运行用例总数大于0
if test_results['total'] > 0:
# 计算用例成功率
test_results["pass_rate"] = round(
(test_results["passed"] + test_results["skipped"]) / test_results["total"] * 100, 2
)
else:
# 如果未运行用例,则成功率为 0.0
test_results["pass_rate"] = 0.0
logger.debug(f"获取到的测试结果:{test_results}")
return test_results
except FileNotFoundError as e:
logger.error(f"程序中检查到您未生成pytest-html报告通常可能导致的原因是pytest-html环境未配置正确{e}")
raise FileNotFoundError(
"程序中检查到您未生成pytest-html报告"
"通常可能导致的原因是pytest-html环境未配置正确"
) from e
def get_test_results_from_from_allure_report(allure_html_path):
"""
从allure生成的html报告的summary.json中获取测试结果及测试情况

View File

@@ -3,15 +3,17 @@
# @Author : chenyinhua
# @File : send_result_handle.py
# @Software: PyCharm
# @Desc: 根据配置文件,发送指定通知
# @Desc: 根据配置文件,发送指定类型的测试结果
# 标准库导入
# 第三方库导入
from loguru import logger
# 本地应用/模块导入
from config.models import NotificationType
from config.settings import SEND_RESULT_TYPE, email, ding_talk, wechat, email_subject, email_content, ding_talk_title, \
ding_talk_content, wechat_content
from case_utils.data_handle import data_handle
from case_utils.get_results_handle import get_test_results_from_pytest_html_report, \
get_test_results_from_from_allure_report
from case_utils.get_results_handle import get_test_results_from_from_allure_report
from common_utils.dingding_handle import DingTalkBot
from common_utils.wechat_handle import WechatBot
from common_utils.yagmail_handle import YagEmailServe
@@ -70,11 +72,10 @@ def send_wechat(webhook_url, content, attachment=None):
logger.error(f"发送企业微信通知异常, 错误信息:{e}")
def send_result(report_path, report_type="allure", attachment_path=None):
def send_result(report_path, attachment_path=None):
"""
根据用户配置,采取指定方式,发送测试结果
:param report_path: 报告路径
:param report_type: 报告类型,根据报告类型获取不同的测试结果, 类型可选值allure, pytest-html
:param attachment_path: 发送的附件, pytest-html就是报告本身作为附件发送 allure是压缩包发送
"""
# 默认不发送任何通知
@@ -82,13 +83,9 @@ def send_result(report_path, report_type="allure", attachment_path=None):
logger.info(f"SEND_RESULT_TYPE={SEND_RESULT_TYPE} 配置了不发送任何邮件")
print(f"SEND_RESULT_TYPE={SEND_RESULT_TYPE} 配置了不发送任何邮件")
return
if report_type.lower() == "allure":
results = get_test_results_from_from_allure_report(report_path)
elif report_type.lower() == "pytest_html":
results = get_test_results_from_pytest_html_report(report_path)
else:
logger.error(f"report_type={report_type} 需要发送的报告类型错误请检查一下配置信息支持的类型为allure, pytest-html")
return
results = get_test_results_from_from_allure_report(report_path)
# 建立发送消息的内容、函数以及参数的映射关系
notification_mappings = {
NotificationType.EMAIL.value: {

View File

@@ -1,122 +0,0 @@
# -*- coding: utf-8 -*-
# @Time : 2023/5/10 14:11
# @Author : chenyinhua
# @File : bs4_handle.py
# @Software: PyCharm
# @Desc: bs4BeautifulSoup4是Python中的第三方库。可以从HTML或XML文件中提取数据的Python库。
from bs4 import BeautifulSoup # pip install beautifulsoup4
class SoupAPI:
def __init__(self, html_file):
self.html_file = html_file
self.soup = self.get_soup()
def get_soup(self):
with open(self.html_file, 'r', encoding='utf-8') as f:
soup = BeautifulSoup(f.read(), 'html.parser')
return soup
def get_element_text(self, element):
"""
获取元素的文本内容
*************************
在使用 BeautifulSoup 库解析 HTML 文件时element.text 和 element.get_text() 都可以用来获取元素的文本内容。它们的区别在于:
element.text 返回的是元素及其子元素的文本内容,但是会将所有的换行符转换为空格。换言之,它可以将文本内容整合成一段连续的字符串,并去掉其中的换行符。
element.get_text() 返回的也是元素及其子元素的文本内容,但是可以通过指定分隔符来保留换行符或者其他字符。
如果不指定分隔符,默认情况下会将所有的空白字符(包括换行符、制表符等)都替换成一个空格。
因此如果你想保留换行符并且不需要在文本内容中保留其他空白字符可以使用element.get_text('\n') 来指定换行符为分隔符。如果你只是想将文本内容整合为一行字符串,可以使用 element.text。
*************************
"""
return element.get_text()
def get_element_by_id(self, element_id):
"""
获取指定ID的元素
"""
element = self.soup.find(id=element_id)
return element
def get_elements_by_class(self, class_name):
"""
获取指定Class的所有元素
"""
elements = self.soup.find_all(class_=class_name)
return elements
def get_elements_by_tag(self, tag_name):
"""
获取指定标签的所有元素
例如self.soup.find_all('p') 获取所有的p标签
"""
elements = self.soup.find_all(tag_name)
return elements
def select_element(self, selector):
"""
使用select筛选select使用CSS选择规则
soup.select(‘标签名'),代表根据标签来筛选出指定标签。
soup.select([属性名="属性值"]'),代表根据指定属性进行筛选;
CSS中#xxx代表筛选idsoup.select(#xxx')代表根据id筛选出指定标签,返回值是一个列表。
CSS中.###代表筛选classsoup.select('.xxx')代表根据class筛选出指定标签,返回值是一个列表。
嵌套select: soup.select(“#xxx .xxxx”),如(“#id2 .news”)就是id=”id2”标签下class=”news的标签返回值是一个列表。
"""
return self.soup.select(selector)
def get_elements_by_attr(self, attr_name, attr_value=True):
"""
通过属性获取标签
如果是查找具体某个属性名称=属性值的标签可以通过attr_name和attr_value来指定
如果仅想查找具备某个属性名称的标签,而不关心属性值是什么,就可以使用 True 来表示属性存在
"""
return self.soup.find_all(attrs={f'{attr_name}': attr_value})
def get_element_parent(self, element):
"""
查找指定元素的父元素;
"""
return element.parent
def get_element_parents(self, element):
"""
查找指定元素的所有祖先元素;
"""
return element.parents
def get_element_next_sibling(self, element):
"""
查找指定元素的下一个兄弟元素;
"""
return element.next_sibling
def get_element_next_siblings(self, element):
"""
查找指定元素的下一个所有的兄弟元素;
"""
return element.next_siblings
def get_element_previous_sibling(self, element):
"""
查找指定元素的上一个兄弟元素;
"""
return element.previous_sibling
def get_element_previous_siblings(self, element):
"""
查找指定元素的上一个所有兄弟元素;
"""
return element.previous_siblings
def get_element_children(self, element):
"""
查找指定元素的所有子元素;
"""
return element.children
def get_element_descendants(self, element):
"""
查找指定元素的所有子孙元素。
"""
return element.descendants

View File

@@ -1,15 +1,18 @@
# -*- coding: utf-8 -*-
# @Time : ${now}
# 标准库导入
# 第三方库导入
import pytest
from loguru import logger
import allure
from loguru import logger
# 本地应用/模块导入
from config.settings import db_info
from config.global_vars import GLOBAL_VARS
from case_utils.assert_handle import assert_response, assert_sql
from case_utils.request_data_handle import RequestPreDataHandle, RequestHandle, after_request_extract
from case_utils.allure_handle import allure_title
from config.settings import db_info
from config.global_vars import GLOBAL_VARS
from pytest_html import extras # 往pytest-html报告中填写额外的内容
# 用例数据
cases = ${case_data}
@@ -22,19 +25,15 @@ class ${class_title}Auto:
@allure.story("${allure_story}")
@pytest.mark.auto
@pytest.mark.parametrize("case", cases, ids=["{}".format(case["title"]) for case in cases])
def ${func_title}_auto(self, case, extra):
def ${func_title}_auto(self, case):
logger.info("\n-----------------------------START-开始执行用例-----------------------------\n")
logger.debug(f"当前执行的用例数据:{case}")
# 添加用例标题作为allure中显示的用例标题
allure_title(case.get("title", ""))
# 处理请求前的用例数据
case_data = RequestPreDataHandle(case).request_data_handle()
# 将用例数据显示在pytest-html报告中
extra.append(extras.text(str(case_data), name="用例数据"))
# 发送请求
response = RequestHandle(case_data).http_request()
# 将响应数据显示在pytest-html报告中
extra.append(extras.text(str(response.text), name="响应数据"))
# 进行响应断言
assert_response(response, case_data["assert_response"])
# 进行数据库断言

View File

@@ -13,10 +13,10 @@ CUSTOM_MARKERS = []
ENV_VARS = {
"common": {
"report_title": "自动化测试报告",
"project_name": "GitLink 确实开源",
"tester": "陈银花",
"department": "开源中心"
"报告标题": "自动化测试报告",
"项目名称": "GitLink 确实开源",
"测试人": "陈银花",
"所属部门": "开源中心"
},
"test": {
# 示例测试环境及示例测试账号

View File

@@ -1,54 +0,0 @@
/* change pytest-html test report style */
@charset "UTF-8";
body {
font-size: 16px;
}
h1 {
text-align: center;
color: #2084D9;
font-size: 30px;
}
h2 {
font-size: 24px;
color: #2084D9;
}
a {
color: #466AFF;
}
span {
font-size: 20px;
}
#environment td {
padding: 10px;
}
#results-table {
font-size: 16px;
table-layout:fixed;
}
#results-table td{
word-break:break-all;
word-wrap:break-word;
}
#results-table th{
font-size: 20px;
background-color: #2084D9;
color: #FFFFFF;
}
td {
color: #000000;
}

View File

@@ -7,62 +7,32 @@
# @Desc: 这是文件的描述信息
# 标准库导入
import re
import time
from time import strftime
# 第三方库导入
from loguru import logger
from py._xmlgen import html # 安装pytest-html版本最好是2.1.1
import pytest
# 本地应用/模块导入
from config.global_vars import ENV_VARS, GLOBAL_VARS, CUSTOM_MARKERS
from config.global_vars import CUSTOM_MARKERS
# ------------------------------------- START: pytest钩子函数处理---------------------------------------#
def pytest_configure(config):
"""
1. 在测试运行前修改Environment部分信息配置测试报告环境信息
2. 注册自定义标记
注册自定义标记
"""
# 给环境表 添加项目名称及开始时间
config._metadata["项目名称"] = ENV_VARS["common"]["project_name"]
config._metadata['开始时间'] = strftime('%Y-%m-%d %H:%M:%S')
# 给环境表 移除packages 及plugins
config._metadata.pop("Packages")
config._metadata.pop("Plugins")
# 注册自定义标记
print(f"需要注册的标记:{CUSTOM_MARKERS}")
logger.debug(f"需要注册的标记:{CUSTOM_MARKERS}")
markers = list(set(CUSTOM_MARKERS))
for custom_marker in markers:
if isinstance(custom_marker, str):
config.addinivalue_line('markers', f'{custom_marker}')
print(f"注册了自定义标记:{custom_marker}")
logger.debug(f"注册了自定义标记:{custom_marker}")
elif isinstance(custom_marker, dict):
for k, v in custom_marker.items():
config.addinivalue_line('markers', f'{k}:{v}')
print(f"注册了自定义标记:{custom_marker}")
@pytest.hookimpl(tryfirst=True)
def pytest_sessionfinish(session, exitstatus):
"""
在测试运行后修改Environment部分信息
"""
# 给环境表 添加 项目环境
session.config._metadata['项目环境'] = GLOBAL_VARS.get("host", "")
@pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
"""设置列"用例描述"的值为用例的标题title"""
outcome = yield
# 获取调用结果的测试报告返回一个report对象
# report对象的属性包括whensteup, call, teardown三个值、nodeid(测试用例的名字)、outcome(用例的执行结果passed,failed)
report = outcome.get_result()
# 将测试用例的title作为测试报告"用例描述"列的值。
# 注意参数传递时需要这样写:@pytest.mark.parametrize("case", cases, ids=["{}".format(case["title"]) for case in cases])
report.description = re.findall('\\[(.*?)\\]', report.nodeid)[0]
report.func = report.nodeid.split("[")[0]
logger.debug(f"注册了自定义标记:{custom_marker}")
def pytest_terminal_summary(terminalreporter, config):
@@ -109,57 +79,4 @@ def pytest_terminal_summary(terminalreporter, config):
f"用例成功率: 0.00 %\n"
"=====================================================")
# ------------------------------------- END: pytest钩子函数处理---------------------------------------#
# ------------------------------------- START: pytest-html钩子函数处理 ---------------------------------------#
def pytest_html_report_title(report):
"""
修改报告标题
"""
report.title = f'{ENV_VARS["common"]["project_name"]} {ENV_VARS["common"]["report_title"]}'
def pytest_html_results_summary(prefix, summary, postfix):
"""
修改Summary部分的信息
"""
prefix.extend([html.p(f'测试人员:{ENV_VARS["common"]["tester"]}')])
prefix.extend([html.p(f'所属部门: {ENV_VARS["common"]["department"]}')])
def pytest_html_results_table_header(cells):
"""
修改结果表的表头
"""
cells.pop(1) # 移除 "Test" 列
# 往表格中增加一列"用例描述",并且给"用例描述"增加排序
cells.insert(0, html.th('用例描述', class_="sortable", col="name"))
# 往表格中增加一列"用例方法",并且给"用例方法"增加排序
cells.insert(1, html.th('用例方法', class_="sortable", col="name"))
# 往表格中增加一列"执行时间",并且给"执行时间"增加排序
cells.insert(2, html.th('执行时间', class_="sortable time", col="time"))
@pytest.mark.optionalhook
def pytest_html_results_table_row(report, cells):
"""
修改结果表的表头后给对应的行增加值
"""
cells.pop(1) # 移除 "Test" 列
# 往列"用例描述"插入每行的值
cells.insert(0, html.td(report.description))
# 往列"用例方法"插入每行的值
cells.insert(1, html.td(report.func))
# 往列"执行时间"插入每行的值
cells.insert(2, html.td(strftime("%Y-%m-%d %H:%M:%S"), class_="col-time"))
def pytest_html_results_table_html(report, data):
"""如果测试通过,则显示这条用例通过啦!"""
if report.passed:
del data[:]
data.append(html.div("这条用例通过啦!", class_="empty log"))
# ------------------------------------- END: pytest-html钩子函数处理 ---------------------------------------#

View File

@@ -4,7 +4,6 @@ addopts =
-s
--cache-clear
--capture=sys
--self-contained-html
--reruns=1
--reruns-delay=5
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True

119
run.py
View File

@@ -13,7 +13,6 @@
> python run.py -m demo 在test环境仅运行打了标记demo用例 默认报告采用allure
> python run.py -env live 在live环境运行测试用例
> python run.py -env=test 在test环境运行测试用例
> python run.py -report=pytest-html (默认在test环境运行测试用例, 报告采用pytest-html)
pytest相关参数以下也可通过pytest.ini配置
--reruns: 失败重跑次数
@@ -94,7 +93,7 @@ def auto_generate_test_cases():
# 封装执行 pytest 的函数
def run_pytest(report_type, mark_param):
def run_pytest(mark_param):
arg_list = []
# 执行指定的测试用例
@@ -104,79 +103,67 @@ def run_pytest(report_type, mark_param):
current_time = datetime.now().strftime("%Y-%m-%d+%H_%M_%S")
# 生成 Allure 报告
if report_type.lower() == "allure":
arg_list.extend(
[
"-q",
"--cache-clear",
f'--alluredir={ALLURE_RESULTS_DIR}',
"--clean-alluredir",
]
)
pytest.main(args=arg_list)
arg_list.extend(
[
"-q",
"--cache-clear",
f'--alluredir={ALLURE_RESULTS_DIR}',
"--clean-alluredir",
]
)
pytest.main(args=arg_list)
# ------------------------ 使用allure生成测试报告 ------------------------
# ------------------------ 使用allure生成测试报告 ------------------------
# 从LIB_DIR目录中寻找以allure开头的目录作为allure模块的目录并进入bin目录下
allure_path = os.path.join(LIB_DIR, [i for i in os.listdir(LIB_DIR) if i.startswith("allure")][0], "bin")
# 根据windows或linux环境判断, 执行指定的命令。
cmd = PlatformHandle().allure[1].format(
os.path.join(allure_path, PlatformHandle().allure[0]),
ALLURE_RESULTS_DIR,
ALLURE_HTML_DIR,
)
os.popen(cmd).read()
# ------------------------ 美化allure测试报告 ------------------------
# 设置allure报告窗口标题
AllureReportBeautiful(allure_html_path=ALLURE_HTML_DIR).set_windows_title(
new_title=ENV_VARS["common"]["project_name"]
)
# 设置allure报告名称
AllureReportBeautiful(allure_html_path=ALLURE_HTML_DIR).set_report_name(
new_name=ENV_VARS["common"]["report_title"]
)
# 往allure测试报告中写入环境配置相关信息
env_info = ENV_VARS["common"]
env_info["run_env"] = GLOBAL_VARS.get("host", "")
AllureReportBeautiful(allure_html_path=ALLURE_HTML_DIR).set_report_env_on_html(
env_info=env_info
)
# 复制http_server.exe以及双击查看报告.bat文件到allure-html根目录下用于支撑电脑在未安装allure服务的情况下打开allure-html报告
# 注意ZIP文件的名称包含某些特殊字符会导致无法使用.bat文件打开allure-html报告 例如空格,/ 等
allure_config_path = os.path.join(CONF_DIR, "allure_config")
# 从LIB_DIR目录中寻找以allure开头的目录作为allure模块的目录并进入bin目录下
allure_path = os.path.join(LIB_DIR, [i for i in os.listdir(LIB_DIR) if i.startswith("allure")][0], "bin")
# 根据windows或linux环境判断, 执行指定的命令。
cmd = PlatformHandle().allure[1].format(
os.path.join(allure_path, PlatformHandle().allure[0]),
ALLURE_RESULTS_DIR,
ALLURE_HTML_DIR,
)
os.popen(cmd).read()
# ------------------------ 美化allure测试报告 ------------------------
# 设置allure报告窗口标题
AllureReportBeautiful(allure_html_path=ALLURE_HTML_DIR).set_windows_title(
new_title=ENV_VARS["common"]["项目名称"]
)
# 设置allure报告名称
AllureReportBeautiful(allure_html_path=ALLURE_HTML_DIR).set_report_name(
new_name=ENV_VARS["common"]["报告标题"]
)
# 往allure测试报告中写入环境配置相关信息
env_info = ENV_VARS["common"]
env_info["运行环境"] = GLOBAL_VARS.get("host", "")
AllureReportBeautiful(allure_html_path=ALLURE_HTML_DIR).set_report_env_on_html(
env_info=env_info
)
# 复制http_server.exe以及双击查看报告.bat文件到allure-html根目录下用于支撑电脑在未安装allure服务的情况下打开allure-html报告
# 注意ZIP文件的名称包含某些特殊字符会导致无法使用.bat文件打开allure-html报告 例如空格,/ 等
allure_config_path = os.path.join(CONF_DIR, "allure_config")
copy_file(src_file_path=os.path.join(allure_config_path,
[i for i in os.listdir(allure_config_path) if i.endswith(".exe")][0]),
dest_dir_path=ALLURE_HTML_DIR)
copy_file(src_file_path=os.path.join(allure_config_path,
[i for i in os.listdir(allure_config_path) if i.endswith(".bat")][0]),
dest_dir_path=ALLURE_HTML_DIR)
# ------------------------ allure测试报告生成完毕压缩allure测试报告为ZIP文件 ------------------------
# report_path以及attachment_path后面发送测试结果需要用到
report_path = ALLURE_HTML_DIR
attachment_path = os.path.join(REPORT_DIR, f'autotest_{str(current_time)}.zip')
# 压缩allure-html报告为一个压缩文件zip
zip_file(in_path=report_path, out_path=attachment_path)
# 生成 pytest-html 报告
else:
report_path = os.path.join(REPORT_DIR, "autotest_" + str(current_time) + ".html")
attachment_path = report_path
pytest_html_config_path = os.path.join(CONF_DIR, "pytest_html_config")
report_css = os.path.join(pytest_html_config_path, "pytest_html_report.css")
arg_list.extend([f"--html={report_path}", f"--css={report_css}"])
pytest.main(args=arg_list)
copy_file(src_file_path=os.path.join(allure_config_path,
[i for i in os.listdir(allure_config_path) if i.endswith(".exe")][0]),
dest_dir_path=ALLURE_HTML_DIR)
copy_file(src_file_path=os.path.join(allure_config_path,
[i for i in os.listdir(allure_config_path) if i.endswith(".bat")][0]),
dest_dir_path=ALLURE_HTML_DIR)
# ------------------------ allure测试报告生成完毕压缩allure测试报告为ZIP文件 ------------------------
# report_path以及attachment_path后面发送测试结果需要用到
report_path = ALLURE_HTML_DIR
attachment_path = os.path.join(REPORT_DIR, f'autotest_{str(current_time)}.zip')
# 压缩allure-html报告为一个压缩文件zip
zip_file(in_path=report_path, out_path=attachment_path)
return report_path, attachment_path
# 主函数
@click.command()
@click.option("-report", default="allure", help="选择需要生成的测试报告pytest-html, allure")
@click.option("-env", default="test", help="输入运行环境test 或 live")
@click.option("-m", default=None, help="选择需要运行的用例python.ini配置的名称")
def run(report, env, m):
def run(env, m):
try:
# ------------------------ 捕获日志----------------------------
capture_all_logs()
@@ -191,11 +178,11 @@ def run(report, env, m):
auto_generate_test_cases()
# ------------------------ pytest执行测试用例 ------------------------
report_path, attachment_path = run_pytest(report_type=report, mark_param=m)
report_path, attachment_path = run_pytest(mark_param=m)
# ------------------------ 发送测试结果 ------------------------
# 发送通知
send_result(report_path, report_type=report, attachment_path=attachment_path)
send_result(report_path, attachment_path=attachment_path)
except Exception as e:
raise e

View File

@@ -6,11 +6,11 @@
# @Software: PyCharm
# @Desc: python脚本编写的测试用例文件
# 第三方库导入
import pytest
from loguru import logger
from pytest_html import extras # 往pytest-html报告中填写额外的内容
import allure
# 本地应用/模块导入
from case_utils.allure_handle import allure_title, custom_allure_step
# 读取用例数据
@@ -21,13 +21,11 @@ cases = [{"title": "demo用例01", "severity": "blocker1", "user": "flora", "age
@allure.story("demo模块(手动用例)")
@pytest.mark.test_demo
@pytest.mark.parametrize("case", cases, ids=["{}".format(case["title"]) for case in cases])
def test_demo(case, extra):
def test_demo(case):
logger.info("\n-----------------------------START-开始执行用例-----------------------------\n")
logger.debug(f"当前执行的用例数据:{case}")
# 添加用例标题作为allure中显示的用例标题
allure_title(case.get("title", ""))
# 将用例数据显示在pytest-html报告中
extra.append(extras.json(case, name="用例数据"))
# 在allure报告中显示请求的用例数据
custom_allure_step(step_title="用例数据", content=f"{case}")
assert case["user"] == "flora"

View File

@@ -6,19 +6,20 @@
# @Software: PyCharm
# @Desc: python脚本编写的测试用例文件
import pytest
# 标准库导入
import os
# 第三方库导入
import pytest
import allure
from loguru import logger
# 本地应用/模块导入
from common_utils.yaml_handle import YamlHandle
from config.path_config import DATA_DIR
from case_utils.assert_handle import assert_response, assert_sql
from loguru import logger
from case_utils.request_data_handle import RequestPreDataHandle, RequestHandle, after_request_extract
from pytest_html import extras # 往pytest-html报告中填写额外的内容
from case_utils.allure_handle import allure_title
import allure
from config.settings import db_info
from config.global_vars import GLOBAL_VARS
from case_utils.assert_handle import assert_response, assert_sql
from case_utils.request_data_handle import RequestPreDataHandle, RequestHandle, after_request_extract
from case_utils.allure_handle import allure_title
# 读取用例数据
yaml_data = YamlHandle(filename=os.path.join(DATA_DIR, "gitlink", "login_demo.yaml")).read_yaml
@@ -36,19 +37,15 @@ class TestLoginDemo:
@allure.story(case_common["allure_story"])
@pytest.mark.test_demo
@pytest.mark.parametrize("case", cases, ids=["{}".format(case["title"]) for case in cases])
def test_login_demo_auto(self, case, extra):
def test_login_demo_auto(self, case):
logger.info("\n-----------------------------START-开始执行用例-----------------------------\n")
logger.debug(f"当前执行的用例数据:{case}")
# 添加用例标题作为allure中显示的用例标题
allure_title(case.get("title", ""))
# 处理请求前的用例数据
case_data = RequestPreDataHandle(case).request_data_handle()
# 将用例数据显示在pytest-html报告中
extra.append(extras.text(str(case_data), name="用例数据"))
# 发送请求
response = RequestHandle(case_data).http_request()
# 将响应数据显示在pytest-html报告中
extra.append(extras.text(str(response.text), name="响应数据"))
# 进行响应断言
assert_response(response, case_data["assert_response"])
# 进行数据库断言