
在业务迭代过程中测试结果散落在各个执行脚本、CI 日志和本地文件中想要回答“最近一次发版质量到底怎么样”都要靠人工汇总效率低而且容易遗漏。当测试平台接入 AI 能力后报告中心就像一个“质量驾驶舱”把测试执行数据收集、指标统计、失败分析和报告展示串成一条完整链路。本文将基于 FastAPI SQLAlchemy SQLite ECharts从零搭建一个 AI 智能测试平台报告中心的可运行演示项目覆盖数据库设计、报告生成、AI 摘要、统计看板和常见问题排查适合测试平台开发者和后端工程师实操参考。1. 背景与核心概念1.1 什么是 AI 智能测试平台AI 智能测试平台并不是一个凭空出现的概念。传统自动化测试平台的核心能力是“调度测试任务、执行测试脚本、收集测试结果”。而 AI 的引入让平台在原有能力之上增加了几个重要方向智能用例生成根据接口定义和历史缺陷数据生成测试用例。智能断言基于模型判断响应内容是否符合预期而不是完全依赖硬编码断言。智能失败分析对自动化执行过程中的失败信息进行聚类、归因辅助开发定位问题。智能质量报告用自然语言总结测试结果给出风险等级和改进建议。报告中心在这些能力中处于“数据出口”的位置。无论测试用例怎么执行、执行引擎在哪个节点跑完最终都需要一个统一的地方存储和展示结果让测试人员、开发人员和项目经理能够快速理解质量状态。1.2 报告中心在测试平台中的定位报告中心是测试平台的子模块也是用户感知最直接的模块。没有报告中心的测试平台就像没有仪表盘的汽车虽然引擎在运转但你不知道油箱还剩多少、水温是否过高。报告中心需要解决三个核心问题数据聚合把来自不同执行节点、不同用例类型接口测试、UI 测试、性能测试的结果统一存储。指标计算实时计算通过率、失败率、耗时趋势、模块分布等质量指标。结果呈现用列表、图表、报告详情等方式把抽象的数据变成用户能快速理解的信息。在 AI 能力加持下报告中心还可以额外输出“AI 摘要”自动总结失败模块、高频错误和建议排查方向。1.3 报告中心整体架构为了让演示项目保持清晰我们把报告中心拆成四层层次职责本演示中用到的技术数据采集层接收测试执行结果POST 接口模拟执行引擎上报数据数据存储层持久化测试批次、用例结果和报告SQLite SQLAlchemy服务层报告生成、指标统计、AI 摘要Python 业务模块展示层看板、报告详情、图表渲染Jinja2 ECharts数据流向可以用一句话概括测试执行引擎把一次完整执行的结果提交到报告中心报告中心落库后计算指标、生成报告前端页面通过接口读取数据并渲染成可视化看板。2. 技术选型与环境准备2.1 技术栈说明对于演示项目我选择了一套轻量、易上手的组合避免引入过重的环境依赖。Python 3.10AI 生态最友好FastAPI 开发效率高。FastAPI基于 Starlette 的现代 Web 框架自带 Swagger 文档写接口非常快速。SQLAlchemy 2.0ORM 框架配合 SQLite 无需额外安装数据库服务。SQLite文件型数据库零配置适合演示和学习。Jinja2FastAPI 支持的模板引擎用于服务端渲染 HTML 页面。ECharts开源可视化图表库用来画通过率趋势、模块分布等图表。版本方面FastAPI 建议使用 0.100 以上版本SQLAlchemy 使用 2.0 以上版本。如果你使用的是 Python 3.8 或更早版本可能需要注意兼容性问题建议直接使用 Python 3.10。2.2 安装依赖新建项目目录并创建虚拟环境mkdir ai-test-report-center cd ai-test-report-center python -m venv venv source venv/bin/activate # Windows 下执行 venv\Scripts\activate创建requirements.txtfastapi0.100,1.0 uvicorn[standard]0.23,1.0 SQLAlchemy2.0,3.0 Jinja23.0,4.0 pydantic2.0,3.0安装依赖pip install -r requirements.txt如果你的网络环境无法直接使用 pip也可以使用国内镜像源安装这里不展开。2.3 项目目录结构ai-test-report-center/ ├── app.py # FastAPI 应用主入口 ├── database.py # 数据库引擎与会话 ├── models.py # SQLAlchemy 数据模型 ├── report_service.py # 报告生成与统计服务 ├── seed.py # 初始化脚本写入模拟数据 ├── requirements.txt # Python 依赖 └── templates/ ├── index.html # 报告中心看板 └── report_detail.html # 报告详情页下面我们按照“数据层 - 服务层 - 接口层 - 展示层”的顺序逐步搭建。3. 数据库设计与核心模型3.1 表结构设计报告中心涉及三类核心数据对象测试批次TestRun一次完整的测试执行包含环境、版本、通过/失败统计等维度信息。用例结果TestCaseResult批次下的每一条用例执行记录记录用例名、模块、状态、错误信息等。报告记录ReportRecord批次对应的报告包含通过率、风险等级、AI 摘要等。三张表的关系是一个批次包含多条用例结果一个批次对应一条报告记录。3.2 模型代码实现创建database.py# database.py from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, declarative_base DATABASE_URL sqlite:///./ai_test_report.db engine create_engine( DATABASE_URL, connect_args{check_same_thread: False} ) SessionLocal sessionmaker(autocommitFalse, autoflushFalse, bindengine) Base declarative_base()创建models.py# models.py from datetime import datetime from sqlalchemy import Column, Integer, String, Float, DateTime, Text, ForeignKey from sqlalchemy.orm import relationship from database import Base class TestRun(Base): 测试执行批次 __tablename__ test_runs id Column(Integer, primary_keyTrue, indexTrue) run_name Column(String(100), nullableFalse, comment测试执行批次名称) environment Column(String(50), defaultstaging, comment执行环境) version Column(String(30), default1.0.0, comment被测系统版本) status Column(String(20), defaultcompleted, comment批次状态) total_count Column(Integer, default0, comment用例总数) passed_count Column(Integer, default0, comment通过数) failed_count Column(Integer, default0, comment失败数) error_count Column(Integer, default0, comment错误数) skipped_count Column(Integer, default0, comment跳过数) duration_seconds Column(Float, default0, comment执行总时长(秒)) created_at Column(DateTime, defaultdatetime.now, comment执行时间) results relationship(TestCaseResult, back_populatesrun, cascadeall, delete-orphan) class TestCaseResult(Base): 单条测试用例执行结果 __tablename__ test_case_results id Column(Integer, primary_keyTrue, indexTrue) run_id Column(Integer, ForeignKey(test_runs.id), indexTrue) case_name Column(String(200), nullableFalse, comment用例名称) module Column(String(100), default, comment所属模块) case_type Column(String(30), defaultapi, comment用例类型) status Column(String(20), nullableFalse, comment执行结果) error_message Column(Text, nullableTrue, comment错误信息) duration_ms Column(Integer, default0, comment用例耗时(ms)) run relationship(TestRun, back_populatesresults) class ReportRecord(Base): 报告记录 __tablename__ report_records id Column(Integer, primary_keyTrue, indexTrue) run_id Column(Integer, ForeignKey(test_runs.id), uniqueTrue, comment关联批次ID) report_name Column(String(100), comment报告名称) pass_rate Column(Float, default0.0, comment通过率) risk_level Column(String(20), defaultlow, comment风险等级) summary Column(Text, comment报告摘要) ai_summary Column(Text, nullableTrue, commentAI智能摘要) created_at Column(DateTime, defaultdatetime.now, comment报告生成时间)这里有几个设计要点需要说明TestCaseResult中的run_id外键和relationship关联让我们可以通过run.results拿到批次下所有用例执行结果。ReportRecord的run_id加了uniqueTrue保证一个测试批次最多对应一份报告避免重复生成。cascadeall, delete-orphan表示删除批次时级联删除用例结果方便在演示中清理测试数据。如果你在实际项目中遇到“报告重复生成”的问题正是因为在设计阶段没有给run_id加唯一约束。4. 核心服务层报告生成与统计4.1 报告生成核心逻辑创建report_service.py这里实现了报告中心最核心的业务逻辑。代码中包含了通过率计算、风险分级、摘要生成和 AI 摘要生成。# report_service.py from datetime import datetime from sqlalchemy.orm import Session from models import TestRun, TestCaseResult, ReportRecord def _calc_pass_rate(run: TestRun) - float: 计算通过率 if run.total_count 0: return 0.0 return round((run.passed_count / run.total_count) * 100, 2) def _risk_level(pass_rate: float, failed_count: int, error_count: int) - str: 根据通过率和失败/错误数量评估风险等级 if error_count 0 or failed_count 5: return high if pass_rate 90: return medium return low def _generate_summary(run: TestRun) - str: 生成结构化摘要 return ( f本轮测试共执行 {run.total_count} 条用例 f通过 {run.passed_count} 条失败 {run.failed_count} 条 f错误 {run.error_count} 条跳过 {run.skipped_count} 条 f总耗时 {run.duration_seconds} 秒。 ) def _generate_ai_summary(db: Session, run: TestRun) - str: 基于规则的AI摘要生成。 真实生产环境中可以将失败用例列表、模块分布、错误信息等结构化数据 作为上下文调用大模型 API 生成更智能的失败分析和改进建议。 这里用规则策略模拟 AI 输出保证演示项目开箱即用。 failed_results ( db.query(TestCaseResult) .filter( TestCaseResult.run_id run.id, TestCaseResult.status.in_([failed, error]), ) .all() ) if not failed_results: return 本轮测试未发现失败用例整体质量状态稳定可进入发布评审阶段。 # 统计失败模块分布 module_failure {} for r in failed_results: module_failure[r.module] module_failure.get(r.module, 0) 1 # 提取高频错误信息 error_counter {} for r in failed_results: if r.error_message: err_key r.error_message.strip().split(\n)[0][:80] error_counter[err_key] error_counter.get(err_key, 0) 1 top_module max(module_failure, keymodule_failure.get) lines [ f本轮测试存在 {len(failed_results)} 条结果异常 f失败集中在模块【{top_module}】。 ] if error_counter: top_error max(error_counter, keyerror_counter.get) lines.append( f高频错误信息为{top_error} f累计出现 {error_counter[top_error]} 次。 ) lines.append(建议优先排查上述模块的代码变更、测试数据残留和服务可用性。) return .join(lines) def generate_report(db: Session, run_id: int) - ReportRecord: 为指定测试批次生成报告如果报告已存在则更新 run db.query(TestRun).filter(TestRun.id run_id).first() if not run: raise ValueError(f测试批次不存在: {run_id}) pass_rate _calc_pass_rate(run) risk _risk_level(pass_rate, run.failed_count, run.error_count) summary _generate_summary(run) ai_summary _generate_ai_summary(db, run) report db.query(ReportRecord).filter(ReportRecord.run_id run_id).first() if report: report.pass_rate pass_rate report.risk_level risk report.summary summary report.ai_summary ai_summary report.created_at datetime.now() else: report ReportRecord( run_idrun.id, report_namef测试报告-{run.run_name}, pass_ratepass_rate, risk_levelrisk, summarysummary, ai_summaryai_summary, ) db.add(report) db.commit() db.refresh(report) return report这里重点解释 AI 摘要的实现思路。演示项目中的_generate_ai_summary函数并不是真正调用了大模型而是通过规则策略对失败用例进行聚类和归因模拟出 AI 分析的效果。这样做的好处是项目不依赖外部 API Key任何环境都能直接运行。在实际生产项目中这个函数可以改造成调用大模型 API 的方式。你需要把失败用例列表、模块分布、错误信息、最近代码变更记录作为 prompt 的上下文让大模型输出更细致的失败原因分析。唯一要注意的是大模型调用会带来额外延迟和成本建议增加缓存和异步处理机制。4.2 统计聚合接口设计报告中心看板需要获取汇总指标和最近趋势。get_dashboard_metrics函数负责聚合所有批次的统计数据def get_dashboard_metrics(db: Session) - dict: 获取报告中心看板所需的所有统计数据 runs db.query(TestRun).all() total_runs len(runs) total_cases sum(r.total_count for r in runs) total_passed sum(r.passed_count for r in runs) total_failed sum(r.failed_count for r in runs) total_error sum(r.error_count for r in runs) total_skipped sum(r.skipped_count for r in runs) pass_rate round(total_passed / total_cases * 100, 2) if total_cases 0 else 0.0 # 最近 7 次执行通过率趋势 recent_runs ( db.query(TestRun) .order_by(TestRun.created_at.desc()) .limit(7) .all() ) trend [ { run_id: r.id, run_name: r.run_name, pass_rate: round(r.passed_count / r.total_count * 100, 2) if r.total_count else 0, created_at: r.created_at.strftime(%m-%d %H:%M), } for r in recent_runs ] trend.reverse() return { total_runs: total_runs, total_cases: total_cases, total_passed: total_passed, total_failed: total_failed, total_error: total_error, total_skipped: total_skipped, pass_rate: pass_rate, trend: trend, } def get_report_detail(db: Session, run_id: int) - dict: 获取报告详情包括用例列表和模块统计 run db.query(TestRun).filter(TestRun.id run_id).first() if not run: return None report db.query(ReportRecord).filter(ReportRecord.run_id run_id).first() results ( db.query(TestCaseResult) .filter(TestCaseResult.run_id run_id) .order_by(TestCaseResult.status, TestCaseResult.module) .all() ) total_results len(results) pass_count sum(1 for r in results if r.status passed) fail_count sum(1 for r in results if r.status failed) error_count sum(1 for r in results if r.status error) skip_count sum(1 for r in results if r.status skipped) module_stats {} for r in results: if r.module not in module_stats: module_stats[r.module] {total: 0, passed: 0, failed: 0, error: 0, skipped: 0} module_stats[r.module][total] 1 if r.status passed: module_stats[r.module][passed] 1 elif r.status failed: module_stats[r.module][failed] 1 elif r.status error: module_stats[r.module][error] 1 else: module_stats[r.module][skipped] 1 return { run: run, report: report, results: results, total_results: total_results, pass_count: pass_count, fail_count: fail_count, error_count: error_count, skip_count: skip_count, pass_rate: report.pass_rate if report else 0, module_stats: module_stats, }4.3 初始化模拟数据为了演示效果我准备了固定的模拟数据。这样每个读这篇文章的人运行seed.py后看到的结果是一致的方便对照验证。创建seed.py# seed.py from datetime import datetime from database import SessionLocal, engine, Base from models import TestRun, TestCaseResult CASES [ # 格式: (模块, 用例名称, 用例类型, 状态, 错误信息, 耗时ms) (用户服务, 登录接口校验, api, passed, None, 120), (用户服务, 注册参数校验, api, failed, AssertionError: 期望status200实际返回500, 80), (用户服务, 获取用户信息, api, passed, None, 60), (用户服务, 修改密码-弱密码校验, api, failed, AssertionError: 弱密码未被拦截, 45), (用户服务, 登录页面UI渲染, ui, passed, None, 320), (用户服务, 注册流程UI测试, ui, failed, ElementNotFound: 未找到注册按钮, 500), (订单服务, 创建订单接口, api, passed, None, 150), (订单服务, 订单列表分页, api, passed, None, 90), (订单服务, 订单状态流转, api, failed, TimeoutError: 请求超时(5000ms), 5000), (订单服务, 订单详情查询, api, passed, None, 70), (订单服务, 订单搜索UI, ui, passed, None, 280), (支付服务, 余额支付, api, passed, None, 200), (支付服务, 支付回调处理, api, passed, None, 160), (支付服务, 支付超时处理, api, passed, None, 110), (支付服务, 支付结果页UI, ui, error, WebDriverException: 浏览器驱动启动失败, 150), (消息服务, 短信发送, api, skipped, 用例被跳过依赖短信平台配置, 0), (消息服务, 站内信发送, api, passed, None, 100), (消息服务, 消息列表查询, api, passed, None, 55), (消息服务, 消息中心UI, ui, passed, None, 260), ] def create_seed_data(run_name: str None) - int: 创建一次测试执行批次数据返回 run_id if run_name is None: run_name fdemo-run-{datetime.now().strftime(%Y%m%d%H%M%S)} db SessionLocal() try: run TestRun( run_namerun_name, environmentstaging, version2.1.0, status