增加allure报告优化类,allure报告增加步骤信息

This commit is contained in:
wangjie 2023-09-27 20:18:56 +08:00
parent 5f3808d8f5
commit fffc80efc2
9 changed files with 264 additions and 74 deletions

View File

@ -7,8 +7,9 @@
import base64 import base64
import json import json
import time import time
from typing import Optional, Tuple, Dict, Any from typing import Optional, Tuple, Any
import allure
import py3curl import py3curl
import requests import requests
from requests import PreparedRequest from requests import PreparedRequest
@ -17,6 +18,7 @@ from requests.structures import CaseInsensitiveDict
from common.base_log import logger from common.base_log import logger
from common.exceptions import ValueNotFoundError from common.exceptions import ValueNotFoundError
from configs.lins_environment import EntryPoint from configs.lins_environment import EntryPoint
from utils.allure_handle import allure_attach_text, allure_attach_json
from utils.time_utils import TimeUtil from utils.time_utils import TimeUtil
@ -64,7 +66,7 @@ class BaseApi:
return params return params
@staticmethod @staticmethod
def request(method, address, headers=None, params=None, data=None, json=None, files=None) -> requests.Response: def request(method, address, headers=None, params=None, data=None, json_data=None, files=None) -> requests.Response:
"""发送http请求返回response对象""" """发送http请求返回response对象"""
# 处理请求参数 # 处理请求参数
url = BaseApi._make_url(address) url = BaseApi._make_url(address)
@ -76,20 +78,41 @@ class BaseApi:
start_time = time.time() # 记录请求开始时间 start_time = time.time() # 记录请求开始时间
# 发起请求 # 发起请求
response = requests.request(method=method, url=url, headers=headers, params=params, response = requests.request(method=method, url=url, headers=headers, params=params,
data=data, json=json, files=files) data=data, json=json_data, files=files)
end_time = time.time() # 记录请求结束时间 end_time = time.time() # 记录请求结束时间
elapsed_time = end_time - start_time # 计算请求时长 duration = end_time - start_time # 计算请求时长
# 记录请求时的详情信息 # 记录请求时的详情信息
r_uri = response.request.url
r_method = method.upper()
r_headers = response.request.headers
r_body = BaseApi.get_request_body(response)
r_curl = BaseApi.request_to_curl(response)
r_respone = response.json()
r_duration = duration
r_respone_status_code = response.status_code
r_respone_headers = response.headers
_log_msg = f"\n==================================================\n" \ _log_msg = f"\n==================================================\n" \
f"请求路径:{response.request.url}\n" \ f"请求路径:{r_uri}\n" \
f"请求方式:{method.upper()}\n" \ f"请求方式:{r_method}\n" \
f"请求头:{response.request.headers}\n" \ f"请求头:{r_headers}\n" \
f"请求内容:{BaseApi.get_request_body(response)}\n" \ f"请求内容:{r_body}\n" \
f"请求curl命令{BaseApi.request_to_curl(response)}\n" \ f"请求curl命令{r_curl}\n" \
f"接口响应内容:{response.json()}\n" \ f"接口响应内容:{r_respone}\n" \
f"接口响应时长:{elapsed_time:.2f}\n" \ f"接口响应头:{r_respone_headers}\n" \
f"HTTP状态码{response.status_code}\n" \ f"接口响应时长:{r_duration:.2f}\n" \
f"HTTP状态码{r_respone_status_code}\n" \
f"==================================================" f"=================================================="
with allure.step("请求内容"):
allure_attach_text("请求路径", f"{r_uri}")
allure_attach_text("请求方式", f"{r_method}")
allure_attach_text("请求头", f"{r_headers}")
allure_attach_json("请求体", f"{r_body}")
allure_attach_text("请求curl命令", f"{r_curl}")
with allure.step("响应内容"):
allure_attach_json("响应体", f"{json.dumps(r_respone, ensure_ascii=False, indent=4)}")
allure_attach_text("HTTP状态码", f"{r_respone_status_code}")
allure_attach_text("响应头", f"{r_respone_headers}")
if response.status_code == 200: if response.status_code == 200:
logger.info(_log_msg) logger.info(_log_msg)
else: else:
@ -107,19 +130,22 @@ class BaseApi:
return BaseApi.request(method='get', address=address, params=params, headers=headers) return BaseApi.request(method='get', address=address, params=params, headers=headers)
@staticmethod @staticmethod
def post(address, data=None, json=None, headers=None, files=None) -> requests.Response: def post(address, data=None, json_data=None, headers=None, files=None) -> requests.Response:
"""发送post请求返回response对象""" """发送post请求返回response对象"""
return BaseApi.request(method='post', address=address, data=data, json=json, headers=headers, files=files) return BaseApi.request(method='post', address=address, data=data, json_data=json_data, headers=headers,
files=files)
@staticmethod @staticmethod
def delete(address, data=None, json=None, headers=None, files=None) -> requests.Response: def delete(address, data=None, json=None, headers=None, files=None) -> requests.Response:
"""发送delete请求返回response对象""" """发送delete请求返回response对象"""
return BaseApi.request(method='delete', address=address, data=data, json=json, headers=headers, files=files) return BaseApi.request(method='delete', address=address, data=data, json_data=json, headers=headers,
files=files)
@staticmethod @staticmethod
def put(address, data=None, json=None, headers=None, files=None) -> requests.Response: def put(address, data=None, json_data=None, headers=None, files=None) -> requests.Response:
"""发送put请求返回response对象""" """发送put请求返回response对象"""
return BaseApi.request(method='put', address=address, data=data, json=json, headers=headers, files=files) return BaseApi.request(method='put', address=address, data=data, json_data=json_data, headers=headers,
files=files)
@staticmethod @staticmethod
def get_json(response: requests.Response) -> json: def get_json(response: requests.Response) -> json:

View File

@ -5,7 +5,7 @@
# @File : models.py # @File : models.py
# @project : SensoroApi # @project : SensoroApi
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum from enum import Enum, unique
from typing import Text from typing import Text
@ -34,3 +34,33 @@ class TestMetrics:
if __name__ == '__main__': if __name__ == '__main__':
print(Environment.DEV.name) print(Environment.DEV.name)
print(Environment.DEV.value) print(Environment.DEV.value)
@unique # 枚举类装饰器,确保只有一个名称绑定到任何一个值。
class AllureAttachmentType(Enum):
"""
allure 报告的文件类型枚举
"""
TEXT = "txt"
CSV = "csv"
TSV = "tsv"
URI_LIST = "uri"
HTML = "html"
XML = "xml"
JSON = "json"
YAML = "yaml"
PCAP = "pcap"
PNG = "png"
JPG = "jpg"
SVG = "svg"
GIF = "gif"
BMP = "bmp"
TIFF = "tiff"
MP4 = "mp4"
OGG = "ogg"
WEBM = "webm"
PDF = "pdf"

View File

@ -4,45 +4,22 @@
# @Author : wangjie # @Author : wangjie
# @File : conftest.py # @File : conftest.py
# @project : SensoroApi # @project : SensoroApi
import os.path
import platform
import time import time
import pytest import pytest
from common.models import TestMetrics from common.models import TestMetrics
from common.settings import ENV
from configs.dir_path_config import BASE_DIR
from configs.lins_environment import EntryPoint
from utils.report_data_handle import ReportDataHandle from utils.report_data_handle import ReportDataHandle
def pytest_sessionstart(): def pytest_sessionstart():
"""在整个pytest运行过程开始之前设置allure报告的环境变量信息""" """在整个pytest运行过程开始之前执行的操作"""
allure_env = { pass
'OperatingEnvironment': ENV.name,
'BaseUrl': EntryPoint.URL(),
'PythonVersion': platform.python_version(),
'Platform': platform.platform(),
'PytestVersion': pytest.__version__,
}
allure_env_file = os.path.join(BASE_DIR, 'environment.properties')
with open(allure_env_file, 'w', encoding='utf-8') as f:
for _k, _v in allure_env.items():
f.write(f'{_k}={_v}\n')
def pytest_sessionfinish(session, exitstatus): def pytest_sessionfinish(session, exitstatus):
"""运行完成后生成allure报告文件再将本地启动方式放入该目录下""" """在整个pytest session结束后执行的操作"""
# # allure报告展示environment时所需要的数据这里是在项目根路径下创建的environment.properties文件拷贝到allure-report报告中,保证环境文件不会被清空 pass
# FileHandle.copy_file(BASE_DIR + os.sep + 'environment.properties', TEMP_DIR)
# # allure报告展示运行器时所需要的数据
# FileHandle.copy_file(BASE_DIR + os.sep + 'executor.json', TEMP_DIR)
# # 使用allure generate -o 命令将./Temp目录下的临时报告生成到Report目录下变成html报告
# os.system(f'allure generate {TEMP_DIR} -o {ALLURE_REPORT_DIR} --clean')
# # 将本地启动脚本和查看allure报告方法放入报告目录下面
# FileHandle.copy_file(BASE_DIR + os.sep + 'open_report.sh', ALLURE_REPORT_DIR)
# FileHandle.copy_file(BASE_DIR + os.sep + '查看allure报告方法', ALLURE_REPORT_DIR)
def pytest_collection_modifyitems(items) -> None: def pytest_collection_modifyitems(items) -> None:

View File

@ -1,5 +0,0 @@
OperatingEnvironment=TEST
BaseUrl=https://www.wanandroid.com
PythonVersion=3.10.4
Platform=macOS-12.3.1-arm64-arm-64bit
PytestVersion=7.4.0

View File

@ -1,10 +0,0 @@
{
"name": "汪杰",
"type": "jenkins",
"url": "http://helloqa.com",
"buildOrder": 3,
"buildName": "allure-report_deploy#1",
"buildUrl": "http://helloqa.com#1",
"reportUrl": "http://helloqa.com#1/AllureReport",
"reportName": "汪杰 Allure Report"
}

View File

@ -15,7 +15,7 @@ class Login(BaseApi):
'password': password 'password': password
} }
return self.post(address, json=json) return self.post(address, json_data=json)
if __name__ == '__main__': if __name__ == '__main__':

16
run.py
View File

@ -16,6 +16,7 @@ import pytest
from common.base_log import logger from common.base_log import logger
from common.models import TestMetrics from common.models import TestMetrics
from utils.allure_handle import AllureReportBeautiful
from utils.command_parser import command_parser from utils.command_parser import command_parser
from common.mail_sender import MailSender from common.mail_sender import MailSender
from common.robot_sender import EnterpriseWechatNotification from common.robot_sender import EnterpriseWechatNotification
@ -58,17 +59,22 @@ if __name__ == '__main__':
# '-m smoke', # 只运行mark标记为smoke的测试用例 # '-m smoke', # 只运行mark标记为smoke的测试用例
]) ])
###################发送allure报告 # ------------------------------发送allure报告----------------------------------
# allure报告展示environment时所需要的数据这里是在项目根路径下创建的environment.properties文件拷贝到allure-report报告中,保证环境文件不会被清空 # 生成allure报告环境信息
FileHandle.copy_file(BASE_DIR + os.sep + 'environment.properties', TEMP_DIR) AllureReportBeautiful.set_report_env_on_results()
# allure报告展示运行器时所需要的数据 # 生成allure报告执行器信息
FileHandle.copy_file(BASE_DIR + os.sep + 'executor.json', TEMP_DIR) AllureReportBeautiful.set_report_executer_on_results()
# 使用allure generate -o 命令将./Temp目录下的临时报告生成到Report目录下变成html报告 # 使用allure generate -o 命令将./Temp目录下的临时报告生成到Report目录下变成html报告
os.system(f'allure generate {TEMP_DIR} -o {ALLURE_REPORT_DIR} --clean') os.system(f'allure generate {TEMP_DIR} -o {ALLURE_REPORT_DIR} --clean')
# 修改allure报告浏览器窗口标题
AllureReportBeautiful.set_windows_title("Sensoro自动化")
# 修改allure报告标题
AllureReportBeautiful.set_report_name("Sensoro自动化测试报告")
# 将本地启动脚本和查看allure报告方法放入报告目录下面 # 将本地启动脚本和查看allure报告方法放入报告目录下面
FileHandle.copy_file(BASE_DIR + os.sep + 'open_report.sh', ALLURE_REPORT_DIR) FileHandle.copy_file(BASE_DIR + os.sep + 'open_report.sh', ALLURE_REPORT_DIR)
FileHandle.copy_file(BASE_DIR + os.sep + '查看allure报告方法', ALLURE_REPORT_DIR) FileHandle.copy_file(BASE_DIR + os.sep + '查看allure报告方法', ALLURE_REPORT_DIR)
# ------------------------------发送通知消息----------------------------------
# 发送企业微信群聊 # 发送企业微信群聊
pytest_result = asdict(TestMetrics(**ReportDataHandle.pytest_json_report_case_count())) pytest_result = asdict(TestMetrics(**ReportDataHandle.pytest_json_report_case_count()))
if IS_SEND_WECHAT: # 判断是否需要发送企业微信 if IS_SEND_WECHAT: # 判断是否需要发送企业微信

View File

@ -4,9 +4,6 @@
# @Author : wangjie # @Author : wangjie
# @File : conftest.py # @File : conftest.py
# @project : SensoroApi # @project : SensoroApi
import os.path
import platform
import allure import allure
import pytest import pytest
@ -14,6 +11,7 @@ from common.base_api import BaseApi
from common.base_log import logger from common.base_log import logger
from common.exceptions import ValueNotFoundError from common.exceptions import ValueNotFoundError
from pageApi.login import Login from pageApi.login import Login
from utils.allure_handle import allure_attach_text
# 定义一个全局变量,用于存储提取的参数内容 # 定义一个全局变量,用于存储提取的参数内容
_global_data = {} _global_data = {}
@ -28,8 +26,9 @@ def set_global_data():
def _set_global_data(cache_name, value): def _set_global_data(cache_name, value):
_global_data[cache_name] = value _global_data[cache_name] = value
allure.attach(str(f"'{cache_name}':'{value}'"), '设置变量:', allure.attachment_type.TEXT) with allure.step("提取"):
allure.attach(str(_global_data), '当前可使用的全局变量:', allure.attachment_type.TEXT) allure_attach_text("设置变量", str(f"'{cache_name}':'{value}'"))
allure_attach_text("当前可使用的全局变量", str(_global_data))
yield _set_global_data yield _set_global_data
_global_data.clear() _global_data.clear()
@ -44,11 +43,12 @@ def get_global_data():
def _get_global_data(cache_data): def _get_global_data(cache_data):
try: try:
allure.attach(str(f"{cache_data}:{_global_data.get(cache_data, None)}"), '取出来的变量:', with allure.step("提取"):
allure.attachment_type.TEXT) allure_attach_text("取出来的变量", str(f"{cache_data}:{_global_data.get(cache_data, None)}"))
return _global_data[cache_data] return _global_data[cache_data]
except KeyError: except KeyError:
allure.attach(str(_global_data), '获取变量失败,当前可使用的全局变量:', allure.attachment_type.TEXT) with allure.step("获取变量失败,当前可使用的全局变量"):
allure_attach_text("获取变量失败,当前可使用的全局变量", str(_global_data))
raise ValueNotFoundError(f"{cache_data}的缓存数据未找到,请检查是否将该数据存入缓存中") raise ValueNotFoundError(f"{cache_data}的缓存数据未找到,请检查是否将该数据存入缓存中")
return _get_global_data return _get_global_data

166
utils/allure_handle.py Normal file
View File

@ -0,0 +1,166 @@
# !/usr/bin/python
# -*- coding:utf-8 -*-
# @Time : 2023/9/27 20:05
# @Author : wangjie
# @File : allure_handle.py
# @project : SensoroApiAutoTest
import json
import os
import platform
import allure
import pytest
from common.models import AllureAttachmentType
from common.settings import ENV
from configs.dir_path_config import TEMP_DIR, ALLURE_REPORT_DIR
from configs.lins_environment import EntryPoint
def allure_title(title: str) -> None:
"""allure中动态生成用例标题"""
# allure.dynamic动态属性
allure.dynamic.title(title)
def allure_attach_text(name: str, body: str = None) -> None:
"""
allure报告添加文本格式附件
:param name: 附件名称
:param body: 附件内容
:return:
"""
allure.attach(body=body, name=name, attachment_type=allure.attachment_type.TEXT)
def allure_attach_json(name: str, body: str = None) -> None:
"""
allure报告添加json格式附件
:param name: 附件名称
:param body: 附件内容
:return:
"""
allure.attach(body=body, name=name, attachment_type=allure.attachment_type.JSON)
def allure_attach_file(name: str, source: str):
"""
allure报告上传附件图片excel等
:param name: 名称
:param source: 文件路径相当于传一个文件
:return:
"""
# 获取上传附件的尾缀,判断对应的 attachment_type 枚举值
_name = source.split('.')[-1]
if _name == "txt" or _name == "uri":
_name = "text" if _name == "txt" else "uri_list"
attachment_type_mapping = {
AllureAttachmentType.TEXT: allure.attachment_type.TEXT,
AllureAttachmentType.CSV: allure.attachment_type.CSV,
AllureAttachmentType.TSV: allure.attachment_type.TSV,
AllureAttachmentType.URI_LIST: allure.attachment_type.URI_LIST,
AllureAttachmentType.HTML: allure.attachment_type.HTML,
AllureAttachmentType.XML: allure.attachment_type.XML,
AllureAttachmentType.JSON: allure.attachment_type.JSON,
AllureAttachmentType.YAML: allure.attachment_type.YAML,
AllureAttachmentType.PCAP: allure.attachment_type.PCAP,
AllureAttachmentType.PNG: allure.attachment_type.PNG,
AllureAttachmentType.JPG: allure.attachment_type.JPG,
AllureAttachmentType.SVG: allure.attachment_type.SVG,
AllureAttachmentType.GIF: allure.attachment_type.GIF,
AllureAttachmentType.BMP: allure.attachment_type.BMP,
AllureAttachmentType.TIFF: allure.attachment_type.TIFF,
AllureAttachmentType.MP4: allure.attachment_type.MP4,
AllureAttachmentType.OGG: allure.attachment_type.OGG,
AllureAttachmentType.WEBM: allure.attachment_type.WEBM,
AllureAttachmentType.PDF: allure.attachment_type.PDF, }
_attachment_type = attachment_type_mapping.get(getattr(AllureAttachmentType, _name.upper(), None), None)
allure.attach.file(source=source, name=name,
attachment_type=_attachment_type,
extension=_name)
class AllureReportBeautiful:
"""
美化allure测试报告
"""
@staticmethod
def set_windows_title(new_title):
"""
设置打开的 Allure 报告的浏览器窗口标题文案
@param new_title: 需要更改的标题文案 原文案为Allure Report
@return:
"""
report_title_filepath = os.path.join(ALLURE_REPORT_DIR, "index.html")
# 定义为只读模型,并定义名称为: f
with open(report_title_filepath, 'r+', encoding="utf-8") as f:
# 读取当前文件的所有内容
all_the_lines = f.readlines()
f.seek(0)
f.truncate()
# 循环遍历每一行的内容,将 "Allure Report" 全部替换为 → new_title(新文案)
for line in all_the_lines:
f.write(line.replace("Allure Report", new_title))
# 关闭文件
f.close()
@staticmethod
def set_report_name(new_name):
"""
修改Allure报告Overview的标题文案
@param new_name: 需要更改的标题文案 原文案为ALLURE REPORT
@return:
"""
title_filepath = os.path.join(ALLURE_REPORT_DIR, "widgets", "summary.json")
# 读取summary.json中的json数据并改写reportName
with open(title_filepath, 'rb') as f:
# 加载json文件中的内容给params
params = json.load(f)
# 修改内容
params['reportName'] = new_name
# 将修改后的内容保存在dict中
new_params = params
# 往summary.json中覆盖写入新的json数据
with open(title_filepath, 'w', encoding="utf-8") as f:
json.dump(new_params, f, ensure_ascii=False, indent=4)
@staticmethod
def set_report_env_on_results():
"""
在allure-results报告的根目录下生成一个写入了环境信息的文件environment.properties(注意不能放置中文否则会出现乱码)
@return:
"""
# 需要写入的环境信息
allure_env = {
'OperatingEnvironment': ENV.name,
'BaseUrl': EntryPoint.URL(),
'PythonVersion': platform.python_version(),
'Platform': platform.platform(),
'PytestVersion': pytest.__version__,
}
allure_env_file = os.path.join(TEMP_DIR, 'environment.properties')
with open(allure_env_file, 'w', encoding='utf-8') as f:
for _k, _v in allure_env.items():
f.write(f'{_k}={_v}\n')
@staticmethod
def set_report_executer_on_results():
"""
在allure-results报告的根目录下生成一个写入了执行人的文件executor.json
@return:
"""
# 需要写入的环境信息
allure_executor = {
"name": "汪杰",
"type": "jenkins",
"url": "http://helloqa.com",
"buildOrder": 3,
"buildName": "allure-report_deploy#1",
"buildUrl": "http://helloqa.com#1",
"reportUrl": "http://helloqa.com#1/AllureReport",
"reportName": "汪杰 Allure Report"
}
allure_env_file = os.path.join(TEMP_DIR, 'executor.json')
with open(allure_env_file, 'w', encoding='utf-8') as f:
f.write(str(json.dumps(allure_executor, ensure_ascii=False, indent=4)))