增加统一配置发送群聊及邮箱配置信息

This commit is contained in:
wangjie 2023-09-08 19:23:07 +08:00
parent cda0cc2b45
commit 9ffdd83d5c
7 changed files with 134 additions and 66 deletions

View File

@ -16,18 +16,17 @@ from common.exceptions import SendMessageError
class MailSender:
"""发送邮件功能"""
def __init__(self, mail_subject, sender_mail_address, sender_username, sender_password, receiver_mail_list,
def __init__(self, mail_subject, sender_username, sender_password, receiver_mail_list,
smtp_domain, smtp_port):
self.subject = mail_subject
self.sender_mail_address = sender_mail_address
self.sender_username = sender_username
self.sender_password = sender_password
self.receiver_mail_list = receiver_mail_list
self.smtp_domain = smtp_domain
self.smtp_port = smtp_port
self.subject = mail_subject # 邮件标题
self.sender_username = sender_username # 发件人邮箱
self.sender_password = sender_password # 发件人邮箱授权码
self.receiver_mail_list = receiver_mail_list # 收件人邮箱
self.smtp_domain = smtp_domain # 发送邮箱的域名
self.smtp_port = smtp_port # 发送邮箱的端口号
self.message = MIMEMultipart()
self.message['From'] = Header(self.sender_mail_address, 'utf-8')
self.message['From'] = Header(self.sender_username, 'utf-8')
self.message['To'] = ';'.join(self.receiver_mail_list)
self.message['subject'] = Header(self.subject, 'utf-8')
@ -67,9 +66,9 @@ class MailSender:
return key
return 'application/octet-stream' # 一切未知类型
def attach_text(self, text_to_send):
def attach_text(self, content):
"""添加邮件正文内容"""
self.message.attach(MIMEText(text_to_send, 'html', 'utf-8'))
self.message.attach(MIMEText(content, 'html', 'utf-8'))
return self
def attach_file(self, file_path):
@ -87,7 +86,7 @@ class MailSender:
# 使用with可以加入超时等待30s并且发送完成后自动关闭链接省去了smtp_obj.quit()步骤
with smtplib.SMTP_SSL(self.smtp_domain, self.smtp_port, timeout=30) as smtp_obj:
smtp_obj.login(self.sender_username, self.sender_password)
smtp_obj.sendmail(from_addr=self.sender_mail_address, to_addrs=self.receiver_mail_list,
smtp_obj.sendmail(from_addr=self.sender_username, to_addrs=self.receiver_mail_list,
msg=self.message.as_string())
logger.info("发送邮件成功")
except smtplib.SMTPException as e:
@ -99,6 +98,6 @@ class MailSender:
if __name__ == '__main__':
MailSender('测试邮件发送', 'xxxxx@xxxx.com', 'xxxxx@xxxx.com', 'xxxxxxx',
MailSender('测试邮件发送', 'xxxxx@xxxx.com', 'xxxxxxx',
['xxxxx@xxxx.com'], 'smtp.exmail.qq.com', 465).attach_text(
'测试邮件').attach_file('/Users/wangjie/SensoroApi/outFiles/pytest_report/report.html').send()

View File

@ -5,23 +5,9 @@
# @File : robot_sender.py
# @project : SensoroApi
import os
import requests
from common.base_log import logger
from common.exceptions import SendMessageError, ValueTypeError
from common.settings import ENV
from utils.reportdatahandle import ReportDataHandle
def get_env_from_jenkins(name, base=''):
"""从Jenkins中获取全局环境变量"""
return os.getenv(name) and os.getenv(name).strip() or base
ProjectName = get_env_from_jenkins("JOB_NAME") # Jenkins构建项目名称
BUILD_URL = get_env_from_jenkins("BUILD_URL") # Jenkins构建项目URL
BUILD_NUMBER = get_env_from_jenkins("BUILD_NUMBER") # Jenkins构建编号
ALLURE_URL = BUILD_URL + 'allure/' # Jenkins构建的allure报告地址
class EnterpriseWechatNotification:
@ -31,7 +17,6 @@ class EnterpriseWechatNotification:
# 企业微信群机器人的hook地址一个机器人就一个多个就定义多个可以写死也可以写在配置类中
self.hook_urls = hook_urls
self.header = {'Content-Type': 'application/json'}
self.pytest_result = ReportDataHandle.pytest_json_report_case_count()
def send_text(self, content, mentioned_mobile_list=None):
"""
@ -57,29 +42,12 @@ class EnterpriseWechatNotification:
else:
raise ValueTypeError("手机号码列表必须是list类型.")
def send_markdown(self, content=''):
def send_markdown(self, content):
"""
发送markdown消息
:param content: markdown格式内容
:return:
"""
content = f"""******用例执行结果统计******
> 项目名称:{ProjectName}
> 构件编号:#{BUILD_NUMBER}
> 测试环境:{ENV.name}
> 总用例数<font color=\"info\">{self.pytest_result['total_case']}条</font>
> 通过用例数<font color=\"info\">{self.pytest_result['pass_case']}条</font>
> 失败用例数<font color=\"red\">{self.pytest_result['fail_case']}条</font>
> 报错用例数<font color=\"red\">{self.pytest_result['error_case']}条</font>
> 跳过用例数<font color=\"warning\">{self.pytest_result['skip_case']}条</font>
> 预期失败用例数<font color=\"comment\">{self.pytest_result['xfail_case']}条</font>
> 预期通过用例数<font color=\"comment\">{self.pytest_result['xpass_case']}条</font>
> 通过率<font color=\"info\">{self.pytest_result['pass_rate']}%</font>
> 用例执行时长<font color=\"info\">{self.pytest_result['case_duration']}s</font>
> 测试报告点击查看>>[测试报告入口]({ALLURE_URL})
> 构建详情点击查看>>[控制台入口]({BUILD_URL})
> {content}"""
payload = {
"msgtype": "markdown",
"markdown": {
@ -130,5 +98,4 @@ class EnterpriseWechatNotification:
if __name__ == '__main__':
EnterpriseWechatNotification(
['hook_url']).send_markdown(
'<@汪杰>')
['hook_url']).send_markdown('<@汪杰>')

View File

@ -6,8 +6,16 @@
# @project : SensoroApi
from common.models import Environment
from utils.jenkins_handle import ProjectName, BUILD_NUMBER, ALLURE_URL, BUILD_URL
from utils.reportdatahandle import ReportDataHandle
# 设置运行的环境变量
'''
开发环境Environment.DEV
测试环境Environment.TEST
生产环境Environment.PROD
点军环境Environment.DIANJUN
'''
ENV = Environment.TEST
# 设置是否需要发送邮件Ture发送False不发送
@ -17,7 +25,70 @@ IS_SEND_EMAIL = False
IS_SEND_WECHAT = False
# 设置是否开启debug日志
LOG_DEBUG = False
LOG_DEBUG = True
# 设置是否开启控制台日志
LOG_CONSOLE = True
# ------------------------------------ 邮件配置信息 ----------------------------------------------------#
# 发送邮件的相关配置信息
email_config = {
'mail_subject': '接口自动化测试报告', # 邮件标题
'sender_username': 'xxxxx@qq.com', # 发件人邮箱
'sender_password': 'ASDsdasda', # 发件人邮箱授权码
'receiver_mail_list': ['xxxxx@qq.com', ], # 收件人邮箱
'smtp_domain': 'smtp.exmail.qq.com', # 发送邮箱的域名
'smtp_port': 465, # 发送邮箱的端口号
}
# TODO:使用模板替换的方式替换该部分内容,否则此处会读取上次执行的报告
# 邮件通知内容
pytest_result = ReportDataHandle.pytest_json_report_case_count()
email_content = f"""
各位同事, 大家好:<br>
自动化用例于 <strong>{pytest_result["case_start_time"]}</strong> 开始运行运行时长<strong>{pytest_result['case_duration']}s</strong> 目前已执行完成<br>
---------------------------------------------------------------------------------------------------------------<br>
项目名称<strong>{ProjectName}</strong> <br>
构件编号<strong>#{BUILD_NUMBER}</strong><br>
项目环境<strong>{ENV.name}</strong><br>
---------------------------------------------------------------------------------------------------------------<br>
执行结果如下:<br>
&nbsp;&nbsp;用例运行总数:<strong> {pytest_result['total_case']}</strong><br>
&nbsp;&nbsp;通过用例个数passed: <strong><font color="green" >{pytest_result['pass_case']}</font></strong><br>
&nbsp;&nbsp;失败用例个数failed: <strong><font color="red" >{pytest_result['fail_case']}</font></strong><br>
&nbsp;&nbsp;报错用例个数error: <strong><font color="orange" >{pytest_result['error_case']}</font></strong><br>
&nbsp;&nbsp;跳过用例个数skipped: <strong><font color="grey" >{pytest_result['skip_case']}</font></strong><br>
&nbsp;&nbsp;预期失败用例个数xfail: <strong><font color="grey" >{pytest_result['xfail_case']}</font></strong><br>
&nbsp;&nbsp;预期通过用例个数xpass: <strong><font color="grey" >{pytest_result['xpass_case']}</font></strong><br>
&nbsp;&nbsp;通过率: <strong><font color="green" >{pytest_result['pass_rate']}%</font></strong><br>
&nbsp;&nbsp;测试报告点击查看: <a href='{ALLURE_URL}'>[测试报告入口]</a><br>
&nbsp;&nbsp;构建详情点击查看: <a href='{BUILD_URL}'>[控制台入口]</a><br>
**********************************<br>
附件为具体的测试报告详细情况可下载附件查看 非相关负责人员可忽略此消息谢谢
"""
# ------------------------------------ 企业微信相关配置 ----------------------------------------------------#
# 企业微信通知群聊
wechat_webhook_url = ["https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxxxx"]
# 企业微信通知内容
wechat_content = f"""******用例执行结果统计******
> 项目名称:{ProjectName}
> 构件编号:#{BUILD_NUMBER}
> 测试环境:{ENV.name}
> 总用例数<font color=\"info\">{pytest_result['total_case']}条</font>
> 通过用例数<font color=\"info\">{pytest_result['pass_case']}条</font>
> 失败用例数<font color=\"red\">{pytest_result['fail_case']}条</font>
> 报错用例数<font color=\"red\">{pytest_result['error_case']}条</font>
> 跳过用例数<font color=\"warning\">{pytest_result['skip_case']}条</font>
> 预期失败用例数<font color=\"comment\">{pytest_result['xfail_case']}条</font>
> 预期通过用例数<font color=\"comment\">{pytest_result['xpass_case']}条</font>
> 通过率<font color=\"info\">{pytest_result['pass_rate']}%</font>
> 用例开始时间:<font color=\"info\">{pytest_result["case_start_time"]}</font>
> 用例执行时长<font color=\"info\">{pytest_result['case_duration']}s</font>
> 测试报告点击查看>>[测试报告入口]({ALLURE_URL})
> 构建详情点击查看>>[控制台入口]({BUILD_URL})
> <@汪杰>"""

View File

@ -7,31 +7,46 @@
import os
# 当前项目的路径
# 当前项目的路径
BASE_DIR = os.path.split(os.path.split(os.path.abspath(__file__))[0])[0]
# common目录的路径
COMMON_DIR = os.path.join(BASE_DIR, 'common')
# configs目录的路径
CONFIGS_DIR = os.path.join(BASE_DIR, 'configs')
# datas目录的路径
DATAS_DIR = os.path.join(BASE_DIR, 'datas')
# pageApi目录的路径
PAGE_API_DIR = os.path.join(BASE_DIR, 'pageApi')
# testCase目录的路径
TEST_CASE_DIR = os.path.join(BASE_DIR, 'testCase')
# utils目录的路径
UTILS_DIR = os.path.join(BASE_DIR, 'utils')
# outFiles目录的路径
OUT_FILES_DIR = os.path.join(BASE_DIR, 'outFiles')
# logs目录的路径
LOGS_DIR = os.path.join(OUT_FILES_DIR, 'logs')
# pytest_report目录的路径
PYTEST_REPORT_DIR = os.path.join(OUT_FILES_DIR, 'pytest_report')
# pytest_result目录的路径
PYTEST_RESULT_DIR = os.path.join(OUT_FILES_DIR, 'pytest_result')
# allure_report目录的路径
ALLURE_REPORT_DIR = os.path.join(OUT_FILES_DIR, 'allure_report')
# screeShot目录的路径
SCREENSHOT_DIR = os.path.join(OUT_FILES_DIR, 'screeShot')
# Temp目录的路径
TEMP_DIR = os.path.join(OUT_FILES_DIR, 'Temp')
if not os.path.exists(TEMP_DIR):
os.makedirs(TEMP_DIR)

26
run.py
View File

@ -17,11 +17,10 @@ from common.base_log import logger
from utils.command_parser import command_parser
from common.mail_sender import MailSender
from common.robot_sender import EnterpriseWechatNotification
from common.settings import IS_SEND_EMAIL, IS_SEND_WECHAT
from configs.dir_path_config import BASE_DIR, TEMP_DIR, ALLURE_REPORT_DIR, PYTEST_REPORT_DIR, CONFIGS_DIR, \
PYTEST_RESULT_DIR
from common.settings import IS_SEND_EMAIL, IS_SEND_WECHAT, wechat_webhook_url, wechat_content, email_content, \
email_config
from configs.dir_path_config import BASE_DIR, TEMP_DIR, PYTEST_REPORT_DIR, PYTEST_RESULT_DIR, ALLURE_REPORT_DIR
from utils.file_handle import FileHandle
from utils.yaml_handle import YamlHandle
if __name__ == '__main__':
logger.info("""
@ -45,9 +44,9 @@ if __name__ == '__main__':
'testCase/', # 执行用例的目录
'--alluredir', f'{TEMP_DIR}', '--clean-alluredir', # 先清空旧的alluredir目录再将生成Allure原始报告需要的数据,并存放在 /Temp 目录
f'--html={os.path.join(PYTEST_REPORT_DIR, "pytest_report.html")}', # 指定pytest-html报告的存放位置
'--self-contained-html', # 将css样式合并到pytest-html报告文件中便于发送邮件
'--json-report', '--json-report-summary', # 生成简化版json报告
f'--json-report-file={os.path.join(PYTEST_RESULT_DIR, "pytest_result.json")}', # 指定json报告存放位置
'--self-contained-html', # 将css样式合并到pytest-html报告文件中便于发送邮件
'--capture=no', # 捕获stderr和stdout这里是使pytest-html中失败的case展示错误日志会导致case中的print不打印
# '-p', 'no:logging', # 表示禁用logging插件使报告中不显示log信息只会显示stderr和stdoyt信息,避免log和stderr重复。
'-p', 'no:sugar', # 禁用pytest-sugar美化控制台结果
@ -68,25 +67,22 @@ if __name__ == '__main__':
# 发送企业微信群聊
if IS_SEND_WECHAT: # 判断是否需要发送企业微信
EnterpriseWechatNotification(
[
'hook_url']).send_markdown(
"<@汪杰>")
EnterpriseWechatNotification(wechat_webhook_url).send_markdown(wechat_content)
# 发送邮件
# 发送邮件
if IS_SEND_EMAIL: # 判断是否需要发送邮件
file_path = PYTEST_REPORT_DIR + os.sep + 'pytest_report.html'
with open(file_path, 'rb') as f:
text_to_send = f.read()
# with open(file_path, 'rb') as f:
# text_to_send = f.read()
config = YamlHandle(CONFIGS_DIR + os.sep + 'mail_config.yaml').read_yaml()
# config = YamlHandle(CONFIGS_DIR + os.sep + 'mail_config.yaml').read_yaml()
config = email_config
ms = MailSender(
mail_subject=config['mail_subject'],
sender_mail_address=config['sender_mail_address'],
sender_username=config['sender_username'],
sender_password=config['sender_password'],
receiver_mail_list=config['receiver_mail_list'],
smtp_domain=config['smtp_domain'],
smtp_port=config['smtp_port'],
)
ms.attach_text(text_to_send).attach_file(file_path).send()
ms.attach_text(email_content).attach_file(file_path).send()

18
utils/jenkins_handle.py Normal file
View File

@ -0,0 +1,18 @@
# !/usr/bin/python
# -*- coding:utf-8 -*-
# @Time : 2023/9/8 19:13
# @Author : wangjie
# @File : jenkins_handle.py
# @project : SensoroApiAutoTest
import os
def get_env_from_jenkins(name, base=''):
"""从Jenkins中获取全局环境变量"""
return os.getenv(name) and os.getenv(name).strip() or base
ProjectName = get_env_from_jenkins("JOB_NAME") # Jenkins构建项目名称
BUILD_URL = get_env_from_jenkins("BUILD_URL") # Jenkins构建项目URL
BUILD_NUMBER = get_env_from_jenkins("BUILD_NUMBER") # Jenkins构建编号
ALLURE_URL = BUILD_URL + 'allure/' # Jenkins构建的allure报告地址

View File

@ -8,6 +8,7 @@ import json
import os
from configs.dir_path_config import BASE_DIR, PYTEST_RESULT_DIR
from utils.time_utils import TimeUtil
class ReportDataHandle:
@ -21,7 +22,7 @@ class ReportDataHandle:
@staticmethod
def pytest_json_report_case_count():
"""统计pytest_json_report报告收集的case数量"""
with open(PYTEST_RESULT_DIR + os.sep+'pytest_result.json', 'r', encoding='utf-8') as f:
with open(PYTEST_RESULT_DIR + os.sep + 'pytest_result.json', 'r', encoding='utf-8') as f:
pytest_result = json.loads(f.read())
case_count = {}
case_count["total_case"] = pytest_result['summary'].get("total", 0) # 用例总数
@ -40,6 +41,7 @@ class ReportDataHandle:
# 如果未运行用例,则成功率为 0.0
case_count["pass_rate"] = 0.0
case_count["case_duration"] = round(pytest_result["duration"], 2) # 用例运行时间
case_count["case_start_time"] = TimeUtil.unix_time_to_str(int('%.f' % pytest_result["created"])) # 用例开始时间
return case_count