增加统一配置发送群聊及邮箱配置信息
This commit is contained in:
parent
cda0cc2b45
commit
9ffdd83d5c
|
@ -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()
|
||||
|
|
|
@ -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('<@汪杰>')
|
||||
|
|
|
@ -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>
|
||||
用例运行总数:<strong> {pytest_result['total_case']}条</strong><br>
|
||||
通过用例个数(passed): <strong><font color="green" >{pytest_result['pass_case']}条</font></strong><br>
|
||||
失败用例个数(failed): <strong><font color="red" >{pytest_result['fail_case']}条</font></strong><br>
|
||||
报错用例个数(error): <strong><font color="orange" >{pytest_result['error_case']}条</font></strong><br>
|
||||
跳过用例个数(skipped): <strong><font color="grey" >{pytest_result['skip_case']}条</font></strong><br>
|
||||
预期失败用例个数(xfail): <strong><font color="grey" >{pytest_result['xfail_case']}条</font></strong><br>
|
||||
预期通过用例个数(xpass): <strong><font color="grey" >{pytest_result['xpass_case']}条</font></strong><br>
|
||||
通过率: <strong><font color="green" >{pytest_result['pass_rate']}%</font></strong><br>
|
||||
测试报告,点击查看: <a href='{ALLURE_URL}'>[测试报告入口]</a><br>
|
||||
构建详情,点击查看: <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})
|
||||
> <@汪杰>"""
|
||||
|
|
|
@ -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
26
run.py
|
@ -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()
|
||||
|
|
|
@ -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报告地址
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue