8. 一键出报告,结果填入HTML模板 配套动画视频在《8. 一键出报告结果填入HTML模板》源代码在《评论区置顶》。截图有了还差一份能打开看的测试报告。这一节把结果填进 HTML 模板先声明工具入参再把输出路径和截图路径规范到工程内有 test_results.json 就读并合并用例截图没有则从文本里解析总计/通过/失败。接着用 Jinja2 把结果填进 templates/report_template.html写出能本地打开的网页并返回通过率任一步异常则返回错误信息。模板要用到 Jinja2提前装好即可pip install Jinja2先定义工具入参test_output 是结果文本output_path 是报告保存路径screenshots 是截图路径列表。调用时要把截图路径传进来否则报告里看不到图。工具名是 generate_test_reportAgent 按这个名字调用class GenerateReportInput(BaseModel): test_output: str Field(description测试输出结果) screenshots: list Field(default_factorylist, description截图路径列表) output_path: str Field(description报告保存路径) class GenerateReportTool(BaseTool): name: str generate_test_report description: str 根据测试结果生成HTML测试报告 args_schema: Type[BaseModel] GenerateReportInput_run 里先做路径规范化。传入的路径可能是 /testcases/...也可能是相对路径。用 _resolve 统一成工程根目录下的绝对路径截图列表里每一项同样处理。screenshots 为 None 时按空列表处理。整个 _run 包在 try/except 里任一步异常都返回错误信息def _run(self, test_output: str, output_path: str, screenshots: list None) - str: base_dir os.path.dirname(os.path.abspath(__file__)) def _resolve(path): if path.startswith(/): return os.path.normpath(os.path.join(base_dir, path.lstrip(/))) return path if os.path.isabs(path) else os.path.normpath( os.path.join(base_dir, path)) output_path _resolve(output_path) if screenshots is None: screenshots [] screenshots [_resolve(s) for s in screenshots]取数时优先看 testcases/test_results.json。有就 json.load并把每个用例 screenshots 里的 path 合并进截图列表。没有 JSON就调用 _parse_output 从 test_output 文本里解析总计、通过和失败results_file os.path.join(base_dir, testcases, test_results.json) if os.path.exists(results_file): with open(results_file, r, encodingutf-8) as f: test_results json.load(f) for tc in test_results.get(test_cases, []): for s in tc.get(screenshots, []): p s.get(path) if isinstance(s, dict) else s if p and p not in screenshots: screenshots.append(p) else: test_results self._parse_output(test_output)渲染阶段根据 passed/total 算通过率 rate把外部传入的 screenshots 与各用例内截图去重合并成 all_shots用 FileSystemLoader 加载 templates/report_template.html把 results、rate、type_labels、all_shots、report_dir 和 to_relative_path 传给模板。to_relative_path 把截图绝对路径转成相对报告文件的路径并把反斜杠换成 /本地打开 HTML 时图片才能显示report_dir os.path.dirname(os.path.abspath(output_path)) rate (results[passed] / results[total] * 100) if results[total] 0 else 0 type_labels {before: 测试前, after: 测试后, failed: 失败时, error: 错误时, step: 步骤截图} all_shots [] seen set() for s in screenshots: if s not in seen: all_shots.append(s) seen.add(s) template_dir os.path.join(os.path.dirname(os.path.abspath(__file__)), templates) env Environment(loaderFileSystemLoader(template_dir)) template env.get_template(report_template.html) def to_relative_path(abs_path: str, base_dir: str) - str: try: rel os.path.relpath(abs_path, base_dir) return rel.replace(\\, /) except Exception: return abs_path.replace(\\, /) html template.render( resultsresults, raterate, type_labelstype_labels, all_shotsall_shots, report_dirreport_dir, to_relative_pathto_relative_path) return html最后调用 _build_html 得到完整 HTML 字符串确保输出目录存在再写入 output_path。返回值带上总计、通过、失败和通过率中间抛异常时返回「生成报告失败…」html self._build_html(test_results, screenshots, output_path) output_file Path(output_path) output_file.parent.mkdir(parentsTrue, exist_okTrue) with open(output_file, w, encodingutf-8) as f: f.write(html) total test_results.get(total, 0) passed test_results.get(passed, 0) failed test_results.get(failed, 0) rate (passed / total * 100) if total 0 else 0 return f报告生成成功{output_path}\n总计:{total} 通过:{passed} f失败:{failed} 通过率:{rate:.1f}%要把启停截图和出报告串起来可以用 open_app_screenshot_close_report先调用 open_app_screenshot_close 拿到 shot_path再拼一份单用例结构的 test_results 写入 test_results.json最后调用 GenerateReportTool._runscreenshots 参数带上 [shot_path]报告页面里才会有这张图shot_path open_app_screenshot_close(screenshot_path) passed 1 if shot_path else 0 failed 0 if shot_path else 1 case_shots [] if shot_path: case_shots.append({type: step, label: app_screenshot, path: shot_path}) test_results { total: 1, passed: passed, failed: failed, test_cases: [{ case_id: TC_OPEN_SHOT_CLOSE, case_name: 启动App截图关闭, status: passed if shot_path else failed, screenshots: case_shots, }], } with open(results_file, w, encodingutf-8) as f: json.dump(test_results, f, ensure_asciiFalse, indent2) shot_list [shot_path] if shot_path else [] report_msg GenerateReportTool()._run(, report_path, shot_list)这样从启停截图到 HTML 报告就接上了本地打开报告文件就能看到通过率和对应截图。配套动画视频在《8. 一键出报告结果填入HTML模板》源代码在《评论区置顶》。

相关新闻

最新新闻

Python实战:爬取世界杯冠军GDP数据,分析体育成就与经济关联

Python实战:爬取世界杯冠军GDP数据,分析体育成就与经济关联

最近在整理世界杯历史数据时,发现一个有趣的现象:足球的荣耀与国家的经济实力之间,似乎存在着某种若隐若现的联系。四年一度的世界杯不仅是球迷的狂欢,其背后也折射出各国综合国力的变迁。本文将带你一起,从数据视角出…

2026/8/9 6:15:48
基于Python与多维度加权算法的个性化音乐榜单生成实战

基于Python与多维度加权算法的个性化音乐榜单生成实战

这次我们来看一个关于华语乐坛收藏排名的项目。这个项目不是技术工具或AI模型,而是一个基于个人音乐收藏和偏好生成的华语乐坛TOP50榜单。它的核心价值在于提供了一个完全由数据驱动的、个性化的音乐排名视角,挑战了传统榜单的权威性,让每个音…

2026/8/9 6:15:48
基于Dify的RAG知识库实战:从零搭建游戏智能助手

基于Dify的RAG知识库实战:从零搭建游戏智能助手

你是不是也遇到过这样的场景:想给团队搭建一个专属知识库,让大模型能准确回答业务问题,结果发现——文档上传了,模型也调了,但回答要么“一本正经地胡说八道”,要么就是答非所问,根本没法用。 …

2026/8/9 6:15:48
Codex模型代理工具:统一管理多AI模型API的本地部署与配置指南

Codex模型代理工具:统一管理多AI模型API的本地部署与配置指南

在实际 AI 开发和应用中,我们经常需要与各种大模型 API 进行交互。无论是调用 OpenAI 的 GPT 系列,还是集成 Claude、DeepSeek 等模型,开发者都面临着一个共同的问题:如何高效、统一地管理这些不同的模型接口,并在本地…

2026/8/9 6:15:48
龙虾自动化品质检测系统核心技术解析

龙虾自动化品质检测系统核心技术解析

1. 龙虾安装测试项目概述最近在海鲜自动化加工领域兴起了一种新型的"龙虾安装测试"技术,这可不是字面意义上的给龙虾装零件,而是一套专门针对活体龙虾品质检测的自动化解决方案。作为一名在海鲜加工行业摸爬滚打多年的技术员,我亲眼…

2026/8/9 6:15:48
宇宙视角地球模拟器:融合卫星数据与AI的气候预测革命

宇宙视角地球模拟器:融合卫星数据与AI的气候预测革命

1. 项目概述:当宇宙视角遇见地球模拟去年在调试气象模型时,我突然意识到一个问题:我们所有的地球模拟系统都建立在"地表视角"上。这就像试图理解一座森林却只观察树根——我们缺少从太空俯瞰地球的整体视角。于是我开始构思一个能融…

2026/8/9 6:10:47