diff --git a/common/mail_sender.py b/common/mail_sender.py
index af246fe..e1508f4 100644
--- a/common/mail_sender.py
+++ b/common/mail_sender.py
@@ -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()
diff --git a/common/robot_sender.py b/common/robot_sender.py
index 92d3a2e..fd3850d 100644
--- a/common/robot_sender.py
+++ b/common/robot_sender.py
@@ -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}
- > 总用例数:{self.pytest_result['total_case']}条
- > 通过用例数:{self.pytest_result['pass_case']}条
- > 失败用例数:{self.pytest_result['fail_case']}条
- > 报错用例数:{self.pytest_result['error_case']}条
- > 跳过用例数:{self.pytest_result['skip_case']}条
- > 预期失败用例数:{self.pytest_result['xfail_case']}条
- > 预期通过用例数:{self.pytest_result['xpass_case']}条
- > 通过率:{self.pytest_result['pass_rate']}%
- > 用例执行时长:{self.pytest_result['case_duration']}s
- > 测试报告,点击查看>>[测试报告入口]({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('<@汪杰>')
diff --git a/common/settings.py b/common/settings.py
index 5d27ed0..d0dd4e2 100644
--- a/common/settings.py
+++ b/common/settings.py
@@ -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"""
+ 各位同事, 大家好:
+
+ 自动化用例于 {pytest_result["case_start_time"]} 开始运行,运行时长:{pytest_result['case_duration']}s, 目前已执行完成。
+ ---------------------------------------------------------------------------------------------------------------
+ 项目名称:{ProjectName}
+ 构件编号:#{BUILD_NUMBER}
+ 项目环境:{ENV.name}
+ ---------------------------------------------------------------------------------------------------------------
+ 执行结果如下:
+ 用例运行总数: {pytest_result['total_case']}条
+ 通过用例个数(passed): {pytest_result['pass_case']}条
+ 失败用例个数(failed): {pytest_result['fail_case']}条
+ 报错用例个数(error): {pytest_result['error_case']}条
+ 跳过用例个数(skipped): {pytest_result['skip_case']}条
+ 预期失败用例个数(xfail): {pytest_result['xfail_case']}条
+ 预期通过用例个数(xpass): {pytest_result['xpass_case']}条
+ 通过率: {pytest_result['pass_rate']}%
+ 测试报告,点击查看: [测试报告入口]
+ 构建详情,点击查看: [控制台入口]
+
+ **********************************
+ 附件为具体的测试报告,详细情况可下载附件查看, 非相关负责人员可忽略此消息。谢谢。
+ """
+
+# ------------------------------------ 企业微信相关配置 ----------------------------------------------------#
+# 企业微信通知群聊
+wechat_webhook_url = ["https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxxxx"]
+
+# 企业微信通知内容
+wechat_content = f"""******用例执行结果统计******
+ > 项目名称:{ProjectName}
+ > 构件编号:#{BUILD_NUMBER}
+ > 测试环境:{ENV.name}
+ > 总用例数:{pytest_result['total_case']}条
+ > 通过用例数:{pytest_result['pass_case']}条
+ > 失败用例数:{pytest_result['fail_case']}条
+ > 报错用例数:{pytest_result['error_case']}条
+ > 跳过用例数:{pytest_result['skip_case']}条
+ > 预期失败用例数:{pytest_result['xfail_case']}条
+ > 预期通过用例数:{pytest_result['xpass_case']}条
+ > 通过率:{pytest_result['pass_rate']}%
+ > 用例开始时间:{pytest_result["case_start_time"]}
+ > 用例执行时长:{pytest_result['case_duration']}s
+ > 测试报告,点击查看>>[测试报告入口]({ALLURE_URL})
+ > 构建详情,点击查看>>[控制台入口]({BUILD_URL})
+ > <@汪杰>"""
diff --git a/configs/dir_path_config.py b/configs/dir_path_config.py
index 5098c0a..5b92154 100644
--- a/configs/dir_path_config.py
+++ b/configs/dir_path_config.py
@@ -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)
diff --git a/run.py b/run.py
index 15c0700..5429bd5 100755
--- a/run.py
+++ b/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()
diff --git a/utils/jenkins_handle.py b/utils/jenkins_handle.py
new file mode 100644
index 0000000..6dff32e
--- /dev/null
+++ b/utils/jenkins_handle.py
@@ -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报告地址
diff --git a/utils/reportdatahandle.py b/utils/reportdatahandle.py
index f365470..815890f 100644
--- a/utils/reportdatahandle.py
+++ b/utils/reportdatahandle.py
@@ -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