Jenkins教程-10-发送飞书测试报告通知

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

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

conftest.py代码如下:

#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

代码如下

# qywechat_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、上一步获取到的测试结果信息,包装成消息体,调用飞书机器人发送群消息接口,自动发送消息到群里

飞书机器人的配置和接口,请参考:开发文档 - 飞书开放平台

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

import json
from datetime import datetime
import jenkins
import requests
import jmespathhost = "http://localhost:8080/"
username = 'admin'
password = 'xxxxx'
webhook = "https://open.feishu.cn/open-apis/bot/v2/hook/c82222qqw64de8a-cac3-4523-b234-85269cf4945d"
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 = "113b81ae7fd66046859f1b9833d391621a"
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)url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"payload = json.dumps({"app_id": "cli_asss6baddbc63b4500c","app_secret": "c39BsssskuRpZqbXzgcyab7fqgRVkTfT7skL"
})
headers = {'Content-Type': 'application/json'
}response = requests.request("POST", url, headers=headers, data=payload).json()
# red = "#FF0000"
# green = "#00ff00"
a = 'green'
b = 'red'
c = 'yellow'
card_demo = {"msg_type": "interactive","card": {"elements": [{"tag": "div","text": {"content": f"-**任务名称**:{job}\n\n-**测试阶段**:{stage}\n\n-**测试结果**:<font color={a if success else b}>{'通过~' if success else '失败!'}</font> {chr(0x1f600) if success else chr(0x1f627)}\n\n-**用例总数**:{total}\n\n-**通过数**:<font color={a}>{passed}</font>\n\n-**通过率**:{passed_ratio}%\n\n-**失败数**:<font color={b}>{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","tag": "lark_md"}}, {"actions": [{"tag": "button","text": {"content": "查看测试报告","tag": "lark_md"},"url": report_url,"type": "primary","value": {"key": "value"}}],"tag": "action"}],"header": {"template": "wathet","title": {"content": "钉钉oapi接口测试任务执行报告通知","tag": "plain_text"}}}
}# payload = json.dumps({
#     "msg_type": "post",
#     "content": {
#         "post": {
#             "zh_cn": {
#                 "title": "钉钉oapi接口测试报告",
#                 "content": [
#                     [
#                         {
#                             "tag": "text",
#                             "text": f"【用例总数】:{total} \n"
#                         },
#                         {
#                             "tag": "text",
#                             "text": f"【测试通过】:{passed} \n"
#                         },
#                         {
#                             "tag": "text",
#                             "text": f"【测试失败】:{failed} \n"
#                         },
#                         {
#                             "tag": "text",
#                             "text": f"【测试错误】:{error} \n"
#                         },
#                         {
#                             "tag": "text",
#                             "text": f"【测试跳过】:{skipped} \n"
#                         },
#                         {
#                             "tag": "text",
#                             "text": f"【测试耗时】:{duration}s \n"
#                         },
#                         {
#                             "tag": "text",
#                             "text": f"【测试时间】:{build_time} \n"
#                         },
#                         {
#                             "tag": "text",
#                             "text": f"【测试结果】: {'通过~' if success else '失败!'}{chr(0x1f600) if success else chr(0x1f627)} \n"
#                         },
#                         {
#                             "tag": "a",
#                             "text": "Allure详细报告,请查看",
#                             "href": f"{report_url}"
#                         }
#                     ]
#                 ]
#             }
#         }
#     }
# })
payload = json.dumps(card_demo)headers = {'Authorization': f"Bearer {response['tenant_access_token']}",'Content-Type': 'application/json'
}requests.request("POST", webhook, headers=headers, data=payload)# 单个报告,详细数据
# http://localhost:8080/job/auto_api_test/76/allure/data/test-cases/9a4eba68509440c8.jsonphone_mapping = {"zhang.san": "13421503860","li.si": "14564591649"
}
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 = []failed_string = f"<font color={b}>【**失败用例**】:\n</font>"
broken_string = f"<font color={c}>【**错误用例**】:\n</font>"for url_info in url_list:name_text = " " + url_info["name"] + "\n"url_text = "[" + " " + url_info["message"] + "]" + "(" + url_info["url"] + ")" + "\n"single_case_text = name_text + url_text# at_dict = {#     "tag": "at",#     "user_id": 1111,# }if url_info["status"] == "failed":failed_string += single_case_text# failed_string_list.append(url_dict)# failed_string_list.append(at_dict)elif url_info["status"] == "broken":failed_string += single_case_text# broken_string_list.append(name_dict)# broken_string_list.append(url_dict)# broken_string_list.append(at_dict)
null_string = " " + f"无\n"
if not failed_list:failed_string += null_string
if not broken_list:broken_string += null_string
# print("failed_string_list", failed_string_list, type(failed_string_list))
# print("broken_string_list", broken_string_list, type(broken_string_list))
# end_string_list = [{
#     "tag": "a",
#     "text": "Allure详细报告,请查看",
#     "href": f"{report_url}"
# }]
all_string = failed_string + broken_string
# print("all_string", all_string_list, type(all_string_list))data_ca_demo = {"msg_type": "interactive","card": {"elements": [{"tag": "div","text": {"content": all_string,"tag": "lark_md"}}, {"actions": [{"tag": "button","text": {"content": "查看测试报告","tag": "lark_md"},"url": report_url,"type": "primary","value": {"key": "value"}}],"tag": "action"}],"header": {"template": "wathet","title": {"content": "钉钉oapi接口测试任务执行错误日志通知","tag": "plain_text"}}}
}
# data_ca = {
#     "msg_type": "post",
#     "content": {
#         "post": {
#             "zh_cn": {
#                 "title": "钉钉oapi接口测试报错信息汇总",
#                 "content": all_string_list
#             }
#         }
#     }
# }
print("data_ca", data_ca_demo)
for author in author_list:if author in list(phone_mapping.keys()):phone_list.append(phone_mapping[author])
print(phone_list)response_r = requests.request("POST", webhook, headers=headers, data=json.dumps(data_ca_demo))
print("response_r", response_r.json())

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

测试结果信息:

错误日志信息:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/35694.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

优化 C# 和 .NET Core Web API 中的 LINQ 查询

LINQ&#xff08;语言集成查询&#xff09;是 C# 中的一项强大功能&#xff0c;允许开发人员以可读且简洁的方式查询和操作数据。但是&#xff0c;LINQ 的使用效率低下可能会导致性能瓶颈&#xff0c;尤其是在处理 .NET Core Web API 中的大型数据集时。优化 LINQ 查询对于维护…

嵌入式 Linux 设备刷系统具体组成

嵌入式 Linux 设备刷系统具体组成 1 介绍1.1 概述1.2 嵌入式 Linux 的组成1.3 U-Boot1.4 Linux 内核1.5 设备树1.6 根文件系统 参考 1 介绍 1.1 概述 一个完整的 linux 系统&#xff0c;通常包含了 U-Boot、kernel、设备树以及根文件系统。 1.2 嵌入式 Linux 的组成 1.3 U-…

Java热门技术点总结:Lambda表达式与Stream API

第一部分&#xff1a;Lambda表达式 1. 简介 Lambda表达式是Java 8引入的一个非常重要的特性&#xff0c;它提供了一种简洁、灵活的函数式编程方式。Lambda表达式允许我们将函数作为参数传递&#xff0c;极大的简化了代码的编写。 2. 基本语法 Lambda表达式的基本语法如下&a…

Java基于jjwt操作jwt

之前讲解了jwt的相关知识&#xff0c;有不了解的&#xff0c;可以查看相关的文章JWT简介-CSDN博客&#xff0c;本节不再介绍&#xff0c;主要讲解有关java中如何通过jjwt库产生jwt以及解析jwt的相关操作。 添加maven依赖 <dependency><groupId>io.jsonwebtoken&l…

目标检测之YoloV1

一、预测阶段&#xff08;前向推断&#xff09; 在预测阶段Yolo就相当于一个黑箱子&#xff0c;输入的是448*448*3的图像&#xff0c;输出是7*7*30的张量&#xff0c;包含了所有预测框的坐标、置信度和类别 为什么是7*7*30呢&#xff1f; --将输入图像划分成s*s个grid cell&a…

【多线程】如何解决线程安全问题?

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. synchronized 关键字1.1 锁是什么1.2 如何加锁1.3 synchronized 修饰方法1) 修饰普通成员方法2) 修饰静态…

【系统架构设计师】七、信息安全技术基础知识(访问控制技术|抗攻击技术|计算机系统安全保护能力等级)

目录 一、访问控制技术 二、信息安全的抗攻击技术 2.1 分布式拒绝服务DDoS与防御 2.3 ARP欺骗攻击与防御 2.4 DNS欺骗与防御 2.5 IP欺骗与防御 2.6 端口扫描&#xff08;Port Scanning&#xff09; 2.7 强化TCP/IP堆栈以抵御拒绝服务攻击 2.8 系统漏洞扫描 三、信息安…

基于weixin小程序乡村旅游系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商家管理&#xff0c;旅游景点管理&#xff0c;景点类型管理&#xff0c;景点路线管理&#xff0c;系统管理 商家帐号账号功能包括&#xff1a;系统首页&#xff0c;旅游景点管理&…

解决RuntimeError: Unsupported image type, must be 8bit gray or RGB image.

今天在使用Opencv进行人脸识别项目时发现了一个问题&#xff0c;一直报这个错误RuntimeError: Unsupported image type, must be 8bit gray or RGB image.查了一下资料也是解决了&#xff0c;这样给大家分享一下 解决方案 Numpy 有一个主要版本更新&#xff0c;与 dlib 不兼容。…

【Docker】创建 swarm 集群

目录 1. 更改防火墙设置 2. 安装 Docker 组件 3. 启动 Docker 服务&#xff0c;并检查服务状态。 4. 修改配置文件&#xff0c;监听同一端口号。 5. 下载 Swarm 组件 6. 创建集群&#xff0c;加入节点 7. 启动集群 8. 查询集群节点信息 9. 查询集群具体信息 10. 查询…

电脑文件concrt140.dll丢失要怎么恢复?靠谱修复方法分析

电脑文件concrt140.dll丢失这种情况&#xff0c;相对来说还是比较少见的&#xff01;但是不代表没有&#xff0c;既然有人出现这种情况了&#xff0c;那么小编势必要给大家详细的讲解一下concrt140.dll这个文件&#xff0c;以及我们要怎么去解决concrt140.dll文件丢失的问题。下…

hnust 1817 算法10-10,10-11:堆排序

hnust 1817 算法10-10,10-11&#xff1a;堆排序 题目描述 堆排序是一种利用堆结构进行排序的方法&#xff0c;它只需要一个记录大小的辅助空间&#xff0c;每个待排序的记录仅需要占用一个存储空间。 首先建立小根堆或大根堆&#xff0c;然后通过利用堆的性质即堆顶的元素是最…

pppd 返回错误码 含义

错误码 00&#xff1a; pppd已经断开&#xff0c;或者已经成功建立连接后请求方又中 断了。 01&#xff1a; 发成了一个严重错误&#xff0c;例如系统调用失败或者访问非法内存。 02&#xff1a; 处理给定操作是检测到错误&#xff0c;例如使用两个互斥的操作。 03&#xff1a;…

如何获取Power BI的个性可视化控件?

我们在使用Power BI Desktop自带可视化控件进行报表设计的时候&#xff0c;有的时候会发现自带控件使用起来略显单薄&#xff0c;需要一些更有创意或者更能直接吸人眼球的可视化控件。 那有没有地方可以让我们找到一些个性化控件呢&#xff1f; 答案是肯定的&#xff0c;目前P…

vscode 安装Vue插件

打开扩展面板 --> 点击左侧的扩展图标&#xff0c;或者按下快捷键 Ctrl Shift X 搜索插件,在搜索框中输入 Vue vue-helper 用来快捷提示&#xff0c;如果使用elementui的话&#xff0c;插件不会自动提示&#xff0c;安装了它&#xff0c;组件、属性都会有提示了 Vetur V…

嵌入式Linux系统编程 — 4.1 字符串输入输出

目录 1 字符串输出 1.1 字符串输出函数简介 1.2 示例程序 2 字符串输入 2.1 字符串输入简介 2.2 示例程序 程序运行时&#xff0c;需打印信息至标准输出 stdout 设备 或标准错误 stderr设备&#xff08;譬如屏幕&#xff09;&#xff0c;如调试信息、报错信息、中间产生的…

Java | Leetcode Java题解之第202题快乐数

题目&#xff1a; 题解&#xff1a; class Solution {private static Set<Integer> cycleMembers new HashSet<>(Arrays.asList(4, 16, 37, 58, 89, 145, 42, 20));public int getNext(int n) {int totalSum 0;while (n > 0) {int d n % 10;n n / 10;totalS…

枫清科技创始人高雪峰:不取侥幸之利,做难而正确的事!丨数据猿专访

大数据产业创新服务媒体 ——聚焦数据 改变商业 金庸有一本著作叫做《侠客行》&#xff0c;这部武侠小说的主角叫做石破天&#xff0c;他从小的时候便跟随少林弟子习武。长大后&#xff0c;随着自己获得的感悟越来越多&#xff0c;最终选择开宗立派&#xff0c;独创一门武功行…

碧海威L7云路由无线运营版 confirm.php/jumper.php 命令注入漏洞复现(XVE-2024-15716)

0x01 产品简介 碧海威L7网络设备是 北京智慧云巅科技有限公司下的产品,基于国产化ARM硬件平台,采用软硬一体协同设计方案,释放出产品最大效能,具有高性能,高扩展,产品性能强劲,具备万兆吞吐能力,支持上万用户同时在线等高性能。其采用简单清晰的可视化WEB管理界面,支持…

【ONLYOFFICE 8.1】的安装与使用——功能全面的 PDF 编辑器、幻灯片版式、优化电子表格的协作

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、引言二、ONLYOFFICE 简介三、安装1. Windows/Mac 安装2. 文档开发者版安装安装前准备使用 Docker 安装使用 Linux 发行版安装配置 ONLYOFFICE 文档开发者版集成和开发 四、使用1. 功能全面的 PDF 编辑器PDF 查看和导航P…