
简介这份资源面向具备Java与Vue基础的中高级研发人员尤其是从事测试平台开发、质量保障与自动化测试框架设计的技术人员围绕端到端自动化测试平台的设计与实现展开解决复杂业务系统回归测试效率低、失败定位难、测试资产分散等痛点可支撑CI/CD质量门禁与测试流程标准化。资源包为1个docx文档压缩包约135KB内容涵盖项目背景、分层架构模型、测试用例与任务调度模型、变量作用域与断言报告机制并给出任务状态枚举、变量解析器、接口请求执行器、断言处理器、任务执行服务等核心代码示例以及数据库表设计、API规范、前后端交互逻辑与部署应用方案。已有92人学习读者可据此掌握页面与接口混合编排、任务调度、变量解析、失败追踪等关键机制并基于完整项目实例进行二次开发或功能扩展。1. 为什么端到端自动化测试平台需要 Java 与 Vue 分工很多团队做自动化测试的起点是一堆散落的脚本Selenium 用例写在 IDE 里Appium 脚本靠某台机器上的环境变量跑通测试报告是本地生成的 HTML 文件谁想看谁去拷。用例一多问题就暴露了——脚本没有统一入口执行结果没有集中存储测试数据散落在各个配置文件里新人接手要先花两天配环境。端到端自动化测试平台要解决的就是这件事把用例管理、任务调度、执行引擎、结果报告、通知告警串成一条流水线。选 Java 做后端、Vue 做前端是这类平台里最常见也最稳的组合。Java 侧有成熟的 Selenium、Appium、TestNG、RestAssured 生态线程池和定时任务调度能力足够撑住并发执行Vue 侧用组件化方式把用例列表、执行看板、报告详情拆开配合 Element Plus 这类 UI 库能快速搭出可用的管理界面。这套组合的读者画像很清晰会写 Java 接口自动化测试框架、能看懂 Vue 路由和组件通信、想把零散脚本升级成平台的中级测试开发以及需要给团队交付一套可维护测试基础设施的技术负责人。平台的核心链路是「前端下任务 → 后端调度 → 执行引擎跑用例 → 结果回写数据库 → 前端轮询或推送展示」。这条链路里每个环节都有坑后面几章会按「数据模型怎么设计、执行引擎怎么跑、前后端怎么对接、报告怎么聚合、并发和稳定性怎么保」的顺序拆开讲。2. 平台数据模型与 Java 后端骨架设计2.1 用例、任务、执行记录三张核心表怎么建平台的数据模型决定了后面所有功能的扩展性。最忌讳的是把用例内容直接塞进一个 text 字段导致无法按标签、模块、优先级检索。常见做法是拆成三张主表加两张关联表。表名作用关键字段test_case用例元信息id, name, module, priority, script_path, tags, statustest_task一次执行任务id, name, case_ids, env, cron_expr, creator, statustest_report执行结果汇总id, task_id, total, passed, failed, skipped, duration, start_timecase_step用例步骤明细id, case_id, step_no, action, locator, valuereport_detail单条用例结果id, report_id, case_id, result, log, screenshottest_case里script_path存的是脚本在仓库中的相对路径而不是脚本内容本身。这样做的好处是脚本可以用 Git 管理平台只负责调度和记录脚本更新走代码评审流程不会因为有人在页面上改了几行就把用例改坏。test_task的case_ids用 JSON 数组存配合 MySQL 5.7 以上的 JSON 类型可以直接用JSON_CONTAINS查询比再建一张关联表更轻。2.2 Spring Boot 分层结构与执行引擎入口后端用 Spring Boot 分四层controller 接请求、service 做业务编排、engine 做执行调度、mapper 做持久化。执行引擎是核心它要解决「一个任务里的多条用例怎么并发跑、跑完怎么汇总」的问题。Service public class ExecutionEngine { // 固定大小线程池大小按机器核数和用例类型调整 private final ExecutorService pool Executors.newFixedThreadPool(8); Autowired private CaseRunner caseRunner; public void runTask(Long taskId, ListLong caseIds) { ListFutureCaseResult futures new ArrayList(); for (Long caseId : caseIds) { // 每条用例提交一个异步任务互不阻塞 futures.add(pool.submit(() - caseRunner.run(caseId))); } ListCaseResult results new ArrayList(); for (FutureCaseResult f : futures) { try { // 设置超时防止单条用例卡死拖垮整个任务 results.add(f.get(5, TimeUnit.MINUTES)); } catch (TimeoutException e) { results.add(CaseResult.timeout()); } catch (Exception e) { results.add(CaseResult.error(e.getMessage())); } } reportService.save(taskId, results); } }这段代码的关键点有三个。第一线程池大小不是越大越好Selenium 每条用例会起一个浏览器实例8 个并发在 4 核 8G 的机器上已经接近上限再大容易 OOM。第二f.get(5, TimeUnit.MINUTES)的超时是必须的UI 自动化里元素等待、页面加载都可能卡住没有超时保护整个任务会挂死。第三结果收集用Future列表而不是CompletionService是因为报告需要按用例顺序展示顺序比吞吐更重要。2.3 用例执行器如何封装 Selenium 与 AppiumCaseRunner要做的是把数据库里的用例步骤翻译成具体操作。UI 用例走 Selenium接口用例走 RestAssured移动端走 Appium用策略模式按case_type分发。public CaseResult run(Long caseId) { TestCase tc caseMapper.selectById(caseId); WebDriver driver null; try { if (WEB.equals(tc.getType())) { driver DriverFactory.createChrome(); for (CaseStep step : stepMapper.listByCaseId(caseId)) { // 按步骤类型执行click / input / assert StepExecutor.execute(driver, step); } } else if (API.equals(tc.getType())) { ApiExecutor.execute(tc); } return CaseResult.pass(caseId); } catch (AssertionError e) { return CaseResult.fail(caseId, e.getMessage()); } finally { // 无论成功失败都要释放浏览器否则进程会堆积 if (driver ! null) driver.quit(); } }DriverFactory里建议用 ThreadLocal 存 driver避免并发时多条用例抢同一个实例。finally里的driver.quit()不能省很多团队跑一段时间发现机器内存被吃满就是因为异常路径下没释放浏览器进程。提示Chrome 用 headless 模式跑 CI 能省资源但部分页面在 headless 下渲染行为和真实浏览器有差异断言前最好加显式等待而不是固定 sleep。3. Vue 前端如何对接执行接口与实时展示结果3.1 用例列表与任务下发页面的组件拆分前端不要把所有逻辑堆在一个页面里。按功能拆成CaseList、TaskForm、ReportView三个主组件用 Vue Router 做路由用 Pinia 管全局状态。用例列表用 Element Plus 的el-table加服务端分页避免一次性拉几千条数据把浏览器卡死。// api/task.js import request from /utils/request export function createTask(data) { // 提交任务后端返回 taskId return request.post(/api/task/create, data) } export function getReport(taskId) { // 轮询报告状态直到 status 为 FINISHED return request.get(/api/report/${taskId}) }request是封装了 axios 的实例统一处理 token 和错误码。任务下发时前端只传caseIds、env、cronExpr三个字段具体怎么跑由后端决定前端不掺和业务逻辑。3.2 用轮询还是 WebSocket 拿执行进度执行进度展示有两种做法。轮询简单前端每 3 秒调一次getReport后端返回当前已完成的用例数和状态WebSocket 实时性好但要处理断线重连和消息顺序。中小团队建议先用轮询实现成本低出问题好排查。// 轮询逻辑组件卸载时清除定时器 startPolling(taskId) { this.timer setInterval(async () { const res await getReport(taskId) this.report res.data if (res.data.status FINISHED || res.data.status FAILED) { clearInterval(this.timer) } }, 3000) }轮询间隔别设太短1 秒一次在任务多的时候会给后端造成不必要的压力。3 到 5 秒是常见取值。组件beforeUnmount里一定要clearInterval否则用户切走页面后定时器还在跑内存泄漏就是这么来的。3.3 报告详情页的失败截图与日志展示报告详情是测试同学看得最多的页面。每条失败用例要能直接看到错误日志和截图而不是让人去服务器上翻文件。后端把截图存到对象存储或本地静态目录数据库只存 URL前端用el-image加预览功能展示。展示项数据来源前端组件用例名称report_detail.case_id 关联el-table-column执行结果report_detail.resultel-tag 按状态着色错误日志report_detail.logel-collapse 折叠展示失败截图report_detail.screenshotel-image 带 preview日志字段可能很长用折叠面板默认收起点击展开。截图用懒加载避免一次渲染几十张图拖慢页面。4. 并发执行、环境隔离与常见故障排查4.1 多任务并发时数据库连接池怎么配平台上线后最容易出问题的地方是并发。多个任务同时跑每个任务又开多条线程数据库连接瞬间被抢光。HikariCP 的maximumPoolSize默认是 10在并发执行场景下明显不够但也不能无脑调大。spring: datasource: hikari: maximum-pool-size: 30 # 按并发任务数 × 每任务连接数估算 minimum-idle: 10 connection-timeout: 3000 # 拿不到连接 3 秒就失败快速暴露问题 max-lifetime: 1800000 # 30 分钟回收避免连接被中间件断开maximum-pool-size的估算方式是并发任务数 × 每个任务同时写库的线程数再留 20% 余量。connection-timeout设短一点让连接不够时快速报错而不是请求全部堆在那里等最后雪崩。4.2 浏览器实例泄漏与端口占用的定位方法跑一段时间后如果发现任务越来越慢八成是浏览器进程没释放。排查步骤很直接# 查看当前机器上的 chrome 进程数 ps -ef | grep chrome | grep -v grep | wc -l # 查看被占用的调试端口 netstat -tlnp | grep 9222 # 批量清理残留进程谨慎使用确认没有正在跑的任务 pkill -f chrome.*headless根因通常是driver.quit()没在finally里执行或者用例超时后线程被中断但浏览器没关。解决办法是在CaseRunner里加一层兜底用try-with-resources或者注册 JVM 关闭钩子在进程退出时统一清理。4.3 用例不稳定Flaky的三种典型原因端到端测试最让人头疼的是同一条用例这次过下次挂。常见原因有三类一是元素定位用了不稳定的 XPath页面结构一变就失效建议优先用>// 不推荐固定等待慢且不可靠 Thread.sleep(3000); driver.findElement(By.id(submit)).click(); // 推荐显式等待条件满足立即继续 WebDriverWait wait new WebDriverWait(driver, Duration.ofSeconds(10)); wait.until(ExpectedConditions.elementToBeClickable(By.id(submit))).click();显式等待的第二个参数是最大等待时间不是固定等待时间条件提前满足就会继续执行整体反而更快。测试数据隔离的做法是每条用例用独立账号或独立数据集跑完清理不要共用。注意Flaky 用例不要靠重试掩盖。重试能提高通过率但会掩盖真实缺陷建议先记录重试次数定期分析高频重试的用例并修复根因。5. 报告聚合、通知告警与平台化收尾技巧5.1 用 SQL 聚合出通过率和趋势数据报告页除了看单次结果团队更关心趋势。通过率、失败用例分布、平均执行时长这些指标用一条 SQL 就能算出来不必在应用层循环统计。-- 按天统计通过率用于趋势图 SELECT DATE(start_time) AS day, COUNT(*) AS total, SUM(CASE WHEN result PASS THEN 1 ELSE 0 END) AS passed, ROUND(SUM(CASE WHEN result PASS THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS pass_rate FROM report_detail WHERE start_time DATE_SUB(NOW(), INTERVAL 30 DAY) GROUP BY DATE(start_time) ORDER BY day;report_detail表在start_time和result上建联合索引数据量上百万后这条查询也能在百毫秒级返回。趋势图前端用 ECharts 折线图展示数据直接吃这个接口的返回。5.2 失败告警接入企业微信或钉钉的触发条件告警不是每次失败都发那样会变成骚扰。合理的触发条件是连续两次执行同一任务失败或者单次失败率超过阈值。告警内容要包含任务名、失败用例数、报告链接让人点进去就能定位。public void checkAndAlert(TestReport report) { double failRate (double) report.getFailed() / report.getTotal(); // 失败率超过 20% 或连续失败才告警 if (failRate 0.2 || isConsecutiveFail(report.getTaskId())) { String msg String.format(任务[%s]失败率%.0f%%失败%d条报告%s, report.getTaskName(), failRate * 100, report.getFailed(), report.getReportUrl()); webhookClient.send(msg); } }isConsecutiveFail查最近两次执行记录都失败才返回 true。阈值和连续次数做成配置项不同团队容忍度不一样硬编码在代码里后面改起来麻烦。5.3 平台从能用走向好用的两个具体技巧第一个技巧是给用例加标签和分组执行。全量回归动辄跑一小时日常只需要跑冒烟集。在test_case表加tags字段任务创建时支持按标签筛选冒烟集打smoke标签回归集打regression日常只跑冒烟发版前跑全量。第二个技巧是执行日志分级存储。report_detail.log字段如果存全量日志一张表很快膨胀到几十 G。做法是数据库只存错误摘要和日志文件路径完整日志写到文件系统或对象存储需要时按路径下载。这样报告表保持轻量查询和聚合都不受影响。-- 日志分级数据库存摘要完整日志存路径 ALTER TABLE report_detail ADD COLUMN log_summary VARCHAR(500) COMMENT 错误摘要, ADD COLUMN log_path VARCHAR(255) COMMENT 完整日志文件路径;这两个改动都不大但对平台长期可维护性的影响很明显。用例规模上百之后没有标签分组和日志分级平台会从帮手变成负担。本文还有配套的精品资源点击获取