Jenkins教程-11-发送钉钉测试报告通知

news/2024/10/5 15:35:20/

上一小节我们学习了发送飞书测试报告通知的方法,本小节我们讲解一下发送钉钉测试报告通知的方法。

1、自动化用例执行完后,使用pytest_terminal_summary钩子函数收集测试结果,存入本地status.txt文件中,供Jenkins调用

#conftest.py def pytest_terminal_summary(terminalreporter, exitstatus, config):"""收集测试报告summary,并存入status.txt文件中,供Jenkins调用"""print("pytest_terminal_summary")passed_num = len([i for i in terminalreporter.stats.get('passed', []) if i.when != 'teardown'])failed_num = len([i for i in terminalreporter.stats.get('failed', []) if i.when != 'teardown'])error_num = len([i for i in terminalreporter.stats.get('error', []) if i.when != 'teardown'])skipped_num = len([i for i in terminalreporter.stats.get('skipped', []) if i.when != 'teardown'])total_num = passed_num + failed_num + error_num + skipped_numtest_result = '测试通过' if total_num == passed_num + skipped_num else '测试失败'duration = round((time.time() - terminalreporter._sessionstarttime), 2)# 定义目录路径directory_path = './reports/'# 确保文件所在的目录存在os.makedirs(os.path.dirname(directory_path), exist_ok=True)# 定义文件路径file_path = os.path.join(directory_path, 'status.txt')with open(file_path, 'w', encoding='utf-8') as f:f.write(f'TEST_TOTAL={total_num}\n')f.write(f'TEST_PASSED={passed_num}\n')f.write(f'TEST_FAILED={failed_num}\n')f.write(f'TEST_ERROR={error_num}\n')f.write(f'TEST_SKIPPED={skipped_num}\n')f.write(f'TEST_DURATION={duration}\n')f.write(f'TEST_RESULT={test_result}\n')

本地文件status.txt中收集测试结果示例:

2、Jenkins中安装Environment Injector 和description setter 插件

Environment Injector插件用于注入环境变量

自动化测试任务配置中,添加构建步骤

填写测试结果收集文件status.txt的路径

description setter用于构建后设置任务描述

将status.txt中的的测试结果字段映射到任务描述中

执行任务构建后,任务描述中会有显示构建的测试结果,如下

3、安装python-jenkins 库,读取自动化测试任务构建后的测试结果描述信息

pip install python-jenkins

代码如下

# dingtalk_remind.py
import json
from datetime import datetime
import jenkins
import requests
import jmespathhost = "http://localhost:8080/"
username = 'admin'
password = 'xxxxxxxxxx'
webhook = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=f4444444444-c1d3-47f2-be78-098f80c2d194"
env = "test"
stage = "回归测试"
job = "auto_api_test"
maintainer = "米兔1号"
server = jenkins.Jenkins(host, username=username, password=password)
last_build_number = server.get_job_info(job)['lastCompletedBuild']['number']
build_info = server.get_build_info(job, last_build_number)
console_url = build_info['url'] + "console"
report_url = build_info['url'] + 'allure'
# report_url = ip_host + report_url.split(":")[-1]
test_status = json.loads(build_info['description'])
print("构建测试结果描述信息:", test_status)total = test_status["total"]
passed = test_status["passed"]
passed_ratio = round(passed / total, 4) * 100
failed = test_status["failed"]
failed_ratio = round((100 - passed_ratio), 2)
error = test_status["error"]
skipped = test_status["skipped"]
duration = test_status["duration"]
build_time = datetime.fromtimestamp(build_info['timestamp'] / 1000).strftime('%Y-%m-%d %H:%M:%S')
success = total == (passed + skipped) if passed != 0 else False

执行上述代码,可以看出,已经获取到jenkins任务的测试结果信息了

4、上一步获取到的测试结果信息,包装成消息体,调用钉钉机器人发送群消息接口,自动发送消息到群里

钉钉群机器人的配置和接口,请参考:自定义机器人发送群消息 - 钉钉开放平台

钉钉发送测试结果消息的整体代码如下:

# -*- coding: utf-8 -*-
import json
from datetime import datetime
import jenkins
import requests
import jmespathhost = "http://localhost:8080/"
username = 'admin'
password = 'xxxxxxxxx'
webhook = "https://oapi.dingtalk.com/robot/send?access_token=bwwwwwwwwwffb1003f5b0ac9d9be5f8c863a38e2d646a69cd231147524829ffbba10e0fa3"
env = "test"
stage = "回归测试"
job = "auto_api_test"
maintainer = "米兔1号"
server = jenkins.Jenkins(host, username=username, password=password)
last_build_number = server.get_job_info(job)['lastCompletedBuild']['number']
build_info = server.get_build_info(job, last_build_number)
print("构建信息:", build_info)
console_url = build_info['url'] + "console"
print("console:", console_url)
report_url = build_info['url'] + 'allure'
print("report_url:", report_url)
test_status = json.loads(build_info['description'])
print("测试结果:", test_status)
total = test_status["total"]
passed = test_status["passed"]
passed_ratio = round(passed / total, 4) * 100
print("passed_ratio", passed_ratio)
failed = test_status["failed"]
failed_ratio = round((100 - passed_ratio),2)
print("failed:", failed_ratio)
error = test_status["error"]
skipped = test_status["skipped"]
duration = test_status["duration"]
build_time = datetime.fromtimestamp(build_info['timestamp'] / 1000).strftime('%Y-%m-%d %H:%M:%S')
success = total == (passed + skipped) if passed != 0 else False# 使用Jenkins API token 模拟登录
USERNAME = "admin"
# Jenkins API token
TOKEN = "1wwwwwww13b81ae7fd66046859f1b9833d391621a"
url_suites = f"{report_url}/data/suites.json"
# print("url_suites", url_suites)
res = requests.get(url_suites, auth=(USERNAME, TOKEN))
# print("res", res.content)
s_url = f"{report_url}/#suites/"
# print('s_url', s_url)
url_raw_list = jmespath.search("children[].children[].children[].children[?status=='failed'||status=='broken'].{name:name,parentUid:parentUid,uid:uid,status:status,tags:tags}",res.json())
# print("url_raw_list", url_raw_list)url_list = []
for raw in url_raw_list[0]:url_dict = {"name": raw["name"], "url": s_url + raw["parentUid"] + "/" + raw["uid"] + "/", "uid": raw["uid"],"status": raw["status"], "author": raw["tags"][0]}url_list.append(url_dict)
# print("url_list", url_list)red = "#FF0000"
green = "#00ff00"
payload = json.dumps({"at": {"isAtAll": "true","atUserIds": ["0113005751430150226"],"atMobiles": []},"actionCard": {"title": "钉钉oapi接口测试任务执行报告通知","text": f"**钉钉oapi接口测试任务执行报告通知** \n\n -**任务名称**:{job}\n\n -**测试阶段**:{stage}\n\n-**测试结果**:<font color={green if success else red}>{'通过~' if success else '失败!'}</font> {chr(0x1f600) if success else chr(0x1f627)}\n\n-**用例总数**:{total}\n\n -**通过数**:<font color={green}>{passed}</font>\n\n-**通过率**:{passed_ratio}%\n\n-**失败数**:<font color={red}>{failed}</font>\n\n-**失败率**:{failed_ratio}%\n\n -**错误数**:{error}\n\n -**跳过数**:{skipped}\n\n -**执行人**:@{maintainer}\n\n-**执行时间**:{build_time}\n\n-**执行耗时**:{duration}s\n\n","btnOrientation": "10","singleTitle": "查看测试报告","singleURL": report_url},"msgtype": "actionCard"
})
headers = {'Content-Type': 'application/json'
}response = requests.request("POST", webhook, headers=headers, data=payload)print(response.json())
# 单个报告,详细数据
# http://localhost:8080/job/auto_api_test/76/allure/data/test-cases/9a4eba68509440c8.jsonphone_mapping = {"zhang.san": "122222203860","li.si": "13454591649"
}
single_url = f"{report_url}/data/test-cases/"
for case in url_list:url = single_url + str(case["uid"]) + ".json"res = requests.get(url, auth=(USERNAME, TOKEN)).json()case["message"] = res["statusMessage"]
print("url_list", url_list)
author_list = list(set(jmespath.search("[*].author", url_list)))
# print("author_list",author_list)
failed_list = jmespath.search("[?status=='failed']", url_list)
print("failed_list", failed_list)
broken_list = jmespath.search("[?status=='broken']", url_list)
print("broken_list", broken_list)
phone_list = []
first_string = f"""**钉钉oapi接口测试任务执行错误日志通知** \n\n"""
failed_info = f"""【**失败用例**】:"""
broken_info = f"""\n\n【**错误用例**】:"""
failed_string = ""
broken_string = ""
for url_info in url_list:if url_info["status"] == "failed":failed_string += f"""\n\n{url_info["name"]}\n\n [{url_info["message"]}]({url_info["url"]})\n\n@{phone_mapping[url_info["author"]]}"""elif url_info["status"] == "broken":broken_string += f"""\n\n{url_info["name"]}\n\n [{url_info["message"]}]({url_info["url"]})\n\n@{phone_mapping[url_info["author"]]}"""
if not failed_string:failed_string = f"\n\n无"
if not broken_string:broken_string = f"\n\n无"
end_string = f""
all_string = first_string + failed_info + failed_string + broken_info + broken_string + end_string
for author in author_list:if author in list(phone_mapping.keys()):phone_list.append(phone_mapping[author])
# print(phone_list)
data_ca = {"at": {"isAtAll": "false","atUserIds": [],"atMobiles": phone_list},"actionCard": {"title": "钉钉oapi接口测试报错信息汇总","text": all_string,"btnOrientation": "10","singleTitle": "查看测试报告","singleURL": report_url},"msgtype": "actionCard"
}
response_r = requests.request("POST", webhook, headers=headers, data=json.dumps(data_ca))
print("response_r", response_r.json())

4、执行上述脚本,查看钉钉通知,如下

测试结果信息:

错误日志信息:

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走,希望可以帮助到大家!


http://www.ppmy.cn/news/1473365.html

相关文章

网络爬虫的原理

网络爬虫的原理 网络爬虫&#xff0c;作为信息检索和数据分析的重要工具&#xff0c;其原理的核心在于模拟人类浏览网页的行为&#xff0c;通过自动化的方式从互联网上收集所需的数据。在了解了网络爬虫的基本原理后&#xff0c;我们可以进一步探讨其在实际应用中的工作机制以…

Java 基础查漏补缺

1.深入解读&#xff1a;JDK与JRE的区别 JDK提供了完整的Java开发工具和资源&#xff0c;包括编译器、调试器和其他开发工具&#xff0c;满足开发人员的各种需求。 JRE则相对更为基础&#xff0c;它只提供了Java程序运行所需的环境&#xff0c;包含了Java虚拟机&#xff08;JVM&…

spring mvc实现一个自定义Converter转换器

介绍 自定义转换器输入Spring MVC框架范畴&#xff0c;总体上输入Spring生态的一个特性&#xff0c;对Web开发起作用。 使用场景 在Spring Boot应用中&#xff0c;自定义转换器主要用于处理HTTP请求参数到Java对象的自动转换&#xff0c;或者Java对象到HTTP响应的序列化过程…

震惊!张宇25版高数18讲发布,656页惹争议!

这个张宇老师在微博已经解释过了&#xff01; 我觉得张宇老师本意是好的&#xff0c;在考研数学教学创新这方面&#xff0c;他真的有自己的思考。 他为什么要这么做&#xff1f; 其实作为一个考研高数老师&#xff0c;他完全可以像其他老师一样&#xff0c;什么都不做&#x…

SpringBoot实战:轻松实现XSS攻击防御(注解和过滤器)

文章目录 引言一、XSS攻击概述1.1 XSS攻击的定义1.2 XSS攻击的类型1.3 XSS攻击的攻击原理及示例 二、Spring Boot中的XSS防御手段2.1 使用注解进行XSS防御2.1.1 引入相关依赖2.1.2 使用XSS注解进行参数校验2.1.3 实现自定义注解处理器2.1.4 使用注解 2.2 使用过滤器进行XSS防御…

python语句前面有一个$是什么意思

“$”是汇编语言中的一个预定义符号&#xff0c;等价于当前正汇编到的段的当前偏移值。例如&#xff1a;指令“jmp $3”中的“$”表示当前这条指令在代码段中的偏移量。 代表当前指令的地址&#xff0c;如&#xff1a; data segment str1 db a,b,c,d leng equ $-str 就是当前地…

解锁水利智慧:智慧水利的深度剖析与未来展望,探讨智慧水利如何助力水利行业实现数字化转型与智能化升级

本文关键词&#xff1a;智慧水利、智慧水利工程、智慧水利发展前景、智慧水利技术、智慧水利信息化系统、智慧水利解决方案、数字水利和智慧水利、数字水利工程、数字水利建设、数字水利概念、人水和协、智慧水库、智慧水库管理平台、智慧水库建设方案、智慧水库解决方案、智慧…

【Spring Boot】Spring AOP中的环绕通知

目录 一、什么是AOP?二、AOP 的环绕通知2.1 切点以及切点表达式2.2 连接点2.3 通知&#xff08;Advice&#xff09;2.4 切面(Aspect)2.5 不同通知类型的区别2.5.1 正常情况下2.5.2异常情况下 2.6 统一管理切点PointCut 一、什么是AOP? Aspect Oriented Programming&#xff…