8. 一键出报告,结果填入HTML模板
配套动画视频在《8. 一键出报告:结果填入HTML模板》,源代码在《评论区置顶》。
截图有了,还差一份能打开看的测试报告。这一节把结果填进 HTML 模板:先声明工具入参,再把输出路径和截图路径规范到工程内;有 test_results.json 就读并合并用例截图,没有则从文本里解析总计/通过/失败。接着用 Jinja2 把结果填进 templates/
report_template.html,写出能本地打开的网页,并返回通过率;任一步异常则返回错误信息。
![[图片]](https://img2024.cnblogs.com/blog/1919157/202608/1919157-20260808194642735-434687096.png)
模板要用到 Jinja2,提前装好即可:
pip install Jinja2
先定义工具入参:test_output 是结果文本,output_path 是报告保存路径,screenshots 是截图路径列表。调用时要把截图路径传进来,否则报告里看不到图。工具名是 generate_test_report,Agent 按这个名字调用:
class GenerateReportInput(BaseModel): test_output: str = Field(description="测试输出结果") screenshots: list = Field(default_factory=list, 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', encoding='utf-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(loader=FileSystemLoader(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( results=results, rate=rate, type_labels=type_labels, all_shots=all_shots, report_dir=report_dir, to_relative_path=to_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(parents=True, exist_ok=True) with open(output_file, 'w', encoding='utf-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._run,screenshots 参数带上 [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', encoding='utf-8') as f: json.dump(test_results, f, ensure_ascii=False, indent=2) shot_list = [shot_path] if shot_path else [] report_msg = GenerateReportTool()._run('', report_path, shot_list)
这样,从启停截图到 HTML 报告就接上了:本地打开报告文件,就能看到通过率和对应截图。
配套动画视频在《8. 一键出报告:结果填入HTML模板》,源代码在《评论区置顶》。

浙公网安备 33010602011771号