diff --git a/case_utils/send_result_handle.py b/case_utils/send_result_handle.py new file mode 100644 index 0000000..534a846 --- /dev/null +++ b/case_utils/send_result_handle.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +# @Time : 2023/5/10 10:43 +# @Author : chenyinhua +# @File : send_result_handle.py +# @Software: PyCharm +# @Desc: +from common_utils.yagmail_handle import YagEmailServe +from config.settings import email +from config.global_vars import NotificationType +from config.settings import SEND_RESULT_TYPE +from common_utils.bs4_handle import SoupAPI + + +def get_test_info_from_html_report(html_report_path): + """ + 从html测试报告中获取测试情况 + """ + bs = SoupAPI(html_report_path) + results = {} + + # -------------- 获取测试环境信息 -------------- + environment_info = bs.get_element_by_id("environment").get_text() + new_environment_info = [element for element in environment_info.split("\n") if element != ''] + for key, value in enumerate(new_environment_info): + if value == "Platform": + results['platform'] = new_environment_info[key + 1] + + if value == "Python": + results['python_version'] = f"Python {new_environment_info[key + 1]}" + + if value == "开始时间": + results['start_time'] = new_environment_info[key + 1] + + if value == "项目名称": + results['project_name'] = new_environment_info[key + 1] + + if value == "项目环境": + results['project_env'] = 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: + results['tester'] = res.replace("测试人员:", "") + if "所属部门: " in res: + results['dept'] = res.replace("所属部门: ", "") + if "tests ran" in res: + new_list = res.split(" ") + results['runs_time'] = f"{new_list[4]} 秒" + + # -------------- 获取具体结果 -------------- + # 通过的用例个数 + passed = bs.select_element('span.passed')[0] + results["passed"] = int(passed.get_text().split(" ")[0]) + # 跳过的用例个数 + skipped = bs.select_element('span.skipped')[0] + results["skipped"] = int(skipped.get_text().split(" ")[0]) + # 失败的用例个数 + failed = bs.select_element('span.failed')[0] + results["failed"] = int(failed.get_text().split(" ")[0]) + # 错误的用例个数 + error = bs.select_element('span.error')[0] + results["error"] = int(error.get_text().split(" ")[0]) + # 预期失败的用例个数 + xfailed = bs.select_element('span.xfailed')[0] + results["xfailed"] = int(xfailed.get_text().split(" ")[0]) + # 意外通过的用例个数 + xpassed = bs.select_element('span.xpassed')[0] + results["xpassed"] = int(xpassed.get_text().split(" ")[0]) + # 重跑的用例个数 + rerun = bs.select_element('span.rerun')[0] + results["rerun"] = int(rerun.get_text().split(" ")[0]) + # 用例总数 + results['total_cases'] = results["passed"] + results["skipped"] + results["failed"] + results["error"] + results[ + "xfailed"] + results["xpassed"] + return results + + +def send_result(results, attachment_path=None): + """ + 根据用户配置,采取指定方式,发送测试结果 + """ + + # 默认不发送任何通知 + if SEND_RESULT_TYPE == NotificationType.DEFAULT.value: + pass + # 发送邮件通知 + elif SEND_RESULT_TYPE == NotificationType.EMAIL.value: + email_settings = email + yag = YagEmailServe(user=email_settings.get("user"), password=email_settings.get("password"), + host=email_settings.get("host")) + info = { + "subject": f"{results.get('project_name', None)} 接口自动化报告_{results.get('start_time', None)}", + "contents": f""" + 各位同事, 大家好: + +     自动化用例于{results.get('start_time', None)}开始运行,运行时长:{results.get('runs_time', None)}, 目前已执行完成。 + ----------------------------------------------------------------------------------------------------------- +     测试人:{results.get('tester', None)} / {results.get('dept', None)} +     测试平台:{results.get('platform', None)} / {results.get('python_version', None)} +     测试环境:{results.get('project_env', None)} + --------------------------------------------------------------------------------------------------------------- +     执行结果如下: +         用例运行总数: {results.get("total_cases", None)} 个 +         通过用例个数(passed): {results.get("passed", None)} 个 +         失败用例个数(failed): {results.get("failed", None)} 个 +         异常用例个数(error): {results.get("error", None)} 个 +         跳过用例个数(skipped): {results.get("skipped", None)} 个 +         预期失败用例个数(xfailed): {results.get("xfailed", None)} 个 +         意外通过用例个数(xpassed): {results.get("xpassed", None)} 个 +         失败重试用例个数 * 次数之和(rerun): {results.get("rerun", None)} 个 +         成 功 率: {(results.get("passed") / results.get("total_cases")) * 100} % + + ********************************** + jenkins地址:https://xxxxxxxxx + 详细情况可登录jenkins平台查看,非相关负责人员可忽略此消息。谢谢。 + """, + "to": email.get("to"), + "attachments": attachment_path + + } + yag.send_email(info) + # 发送钉钉通知 + elif SEND_RESULT_TYPE == NotificationType.DING_TALK.value: + pass + # 发送企业微信通知 + elif SEND_RESULT_TYPE == NotificationType.WECHAT.value: + pass + # 全部渠道都发送通知 + else: + pass diff --git a/config/global_vars.py b/config/global_vars.py index 7282464..05cbbc1 100644 --- a/config/global_vars.py +++ b/config/global_vars.py @@ -19,3 +19,13 @@ class CaseFileType(Enum): YAML = 1 EXCEL = 2 ALL = 0 + + +class NotificationType(Enum): + """ 自动化通知方式 """ + DEFAULT = 0 + DING_TALK = 1 + WECHAT = 2 + EMAIL = 3 + ALL = 4 + diff --git a/config/settings.py b/config/settings.py index 5134822..d6c8a5a 100644 --- a/config/settings.py +++ b/config/settings.py @@ -6,9 +6,13 @@ # @Software: PyCharm # @Desc: 项目配置文件 - +# ------------------------------------ 配置信息 ----------------------------------------------------# # 0代表执行Excel和yaml两种格式的用例, 1 代表 yaml文件,2 用例代表Excel用例 CASE_FILE_TYPE = 0 + +# 0表示默认不发送任何通知, 1代表钉钉通知,2代表企业微信通知, 3代表邮件通知, 4代表所有途径都发送通知 +SEND_RESULT_TYPE = 3 + # 测试报告的定制化信息展示 REPORT_TITLE = "自动化测试报告" REPORT_NAME = f"apiautotest-report-" @@ -19,7 +23,7 @@ DEPARTMENT = "所属部门: 开源中心" # 指定日志收集级别 LOG_LEVEL = "INFO" -# 测试数据 +# ------------------------------------ 测试数据 ----------------------------------------------------# test = [ { # 示例测试环境及示例测试账号 @@ -44,3 +48,13 @@ live = [ "project": "" } ] + +# ------------------------------------ 邮件配置信息 ----------------------------------------------------# + +# 发送邮件的相关配置信息 +email = { + "user": "******", # 发件人邮箱 + "password": "******", # 发件人邮箱授权码 + "host": "smtp.qq.com", + "to": ["******", "******"] # 收件人邮箱 +} diff --git a/conftest.py b/conftest.py index e9fd022..9db0eec 100644 --- a/conftest.py +++ b/conftest.py @@ -13,6 +13,7 @@ import pytest from py._xmlgen import html # 安装pytest-html,版本最好是2.1.1 from time import strftime from config.settings import test, live, REPORT_TITLE, PROJECT_NAME, TESTER, DEPARTMENT +from case_utils.send_result_handle import get_test_info_from_html_report, send_result # ------------------------------------- START: 配置运行环境 ---------------------------------------# @@ -54,6 +55,7 @@ def get_config(request): # ------------------------------------- END: 配置运行环境 ---------------------------------------# + # ------------------------------------- START: 报告处理 ---------------------------------------# def pytest_collection_modifyitems(items): """# 测试用例执行收集完成时,将收集到的item的name和nodeid的中文显示在控制台上""" @@ -71,7 +73,6 @@ def pytest_runtest_makereport(item, call): report = outcome.get_result() # TODO:由于目前无法动态将用例数据中的title写入测试方法中的文档注释,因此该处理方法暂时搁置 # 将测试方法的文档注释作为结果表的Description的值,如果文档注释为空,则测试方法名作为结果表的Description的值 - logger.debug(f"文档注释:{item.function.__doc__}") report.description = str(item.function.__doc__) report.nodeid = report.nodeid.encode("utf-8").decode("unicode_escape") @@ -104,6 +105,15 @@ def pytest_sessionfinish(session, exitstatus): session.config._metadata['项目环境'] = GLOBAL_VARS.get("host", "") +@pytest.hookimpl(hookwrapper=True) +def pytest_terminal_summary(terminalreporter, exitstatus, config): + yield + # 获取测试报告路径,并发送测试结果 + report_path = config.getoption('--html') + results = get_test_info_from_html_report(report_path) + send_result(results, report_path) + + def pytest_html_results_summary(prefix, summary, postfix): """ 修改Summary部分的信息