Expect Watch模式工作原理深度剖析:代码变更如何智能触发浏览器回归测试
【免费下载链接】expectExpect tests your agent's code in a real browser项目地址: https://gitcode.com/gh_mirrors/expect6/expect
Expect 是一个让 AI 编程智能体在真实浏览器中自动回归测试你代码的开源工具。它的Watch 模式是关键亮点:终端里挂起一个监控循环,自动捕获 git 代码变更,由 AI 评估这些改动是否影响用户可见行为,再智能地触发一次真实浏览器回归测试。本文基于源码深度剖析 Expect Watch 模式的工作原理,讲清楚代码变更是如何被捕捉、过滤、评估并最终触发测试的。
什么是 Expect Watch 模式:一个不下班的"浏览器测试哨兵"
Expect 的核心思路是:读取你的 git 变更 → 生成测试计划 → 用 Playwright 驱动真实浏览器执行,覆盖性能(LCP、INP)、安全、设计细节(悬停态、死链)和应用完整性检查。
而Watch 模式把"跑一次"升级为"常驻监控":你在开发循环中持续改动代码,Expect 负责判断哪些改动值得测试,并在该测的时候自动开测。入口命令很简单(源码:apps/cli/src/commands/watch.ts):
expect watch:默认监控changes(相对 main 分支的全部差异)-t, --target:可切换为unstaged(未暂存改动)或branch(分支差异)-m, --message:自定义自然语言测试指令,默认是"在浏览器中测试 main 的所有变更并确认它们正确工作"-a, --agent:指定评估/执行用的智能体(Claude Code、Codex 等),自动检测本机已装智能体
核心机制全景图:轮询 → 稳定化 → 智能评估 → 执行
整个 Watch 循环由 packages/supervisor/src/watch.ts 中的Watch服务驱动,是一条由事件串联的流水线。源码中定义了完整的WatchEvent事件族:Polling → ChangeDetected → Settling → Assessing → RunStarting → RunUpdate → RunCompleted(以及Skipped/Error/Stopped),每一步都有明确的状态。
第一步:每 2 秒为仓库"计算指纹"
轮询间隔为2000ms(常量WATCH_POLL_INTERVAL_MS)。每次轮询不是盲目地"看文件有没有变",而是调用computeFingerprint计算一个仓库指纹:
- 指纹 =
SHA-256(HEAD 提交 + 未暂存 diff + 已暂存 diff),实现见 git/git.ts - 若指纹与"上一次测试过的指纹"相同 → 只发一个
Polling事件,什么都不做 - 若不同 → 发出
ChangeDetected,进入下一关
指纹机制让"有没有变化"的判断是 O(1) 的字符串比较,无需扫描文件树。
第二步:3 秒"稳定化窗口",拒绝测试半成品
检测到变更后,Watch 并不立刻行动,而是进入Settling 阶段等待 3000ms(WATCH_SETTLE_DELAY_MS),然后重新计算指纹:
- 指纹没变 → 说明改动已落盘稳定,继续流水线
- 指纹变了 → 说明你还在继续改(典型的"边打字边保存"场景),本轮直接放弃,等待下一轮轮询覆盖新状态
这个小而精的设计避免了对"半截保存"的代码跑测试,也天然实现了变更防抖(debounce):你连改三次,只有停下来 3 秒后才会触发一次测试。
第三步:双层决策——确定性过滤 + AI 评估
这是 Watch 模式"智能"的核心。决策分两层,先便宜后昂贵:
第一层:确定性分类(零成本)
classifyDeterministically(watch.ts)会检查本次变更的所有文件是否都命中"无需测试"清单:
| 类型 | 示例 |
|---|---|
| 文档/纯文本 | .md、.txt、.mdx |
| 配置样板 | .prettierrc、.eslintignore、.editorconfig |
| 锁文件 | package-lock.json、*.lock、lock.yaml |
| 工具目录 | .github/(copilot 除外)、.husky/、.vscode/、.cursor/、.idea/ |
| 其他 | LICENSE、CHANGELOG、.env.example |
只要全部变更文件都命中,直接skip——连 AI 都不调用。
第二层:AI 分类(只处理真正模糊的改动)
对剩下的改动,Watch 会构建一个专门的分类提示词(buildWatchAssessmentPrompt,prompts.ts),把变更文件列表 + diff 预览 + 你的测试指令交给智能体,要求它只回答一个单词:
run— 改动影响用户可见行为(UI、路由、API 调用、样式、文案、影响运行时行为的配置)skip— 纯内部改动(注释、纯类型重构、仅测试文件、文档、锁文件、CI 配置)
提示词里写死了两条关键规则:拿不准就 run、不要解释理由。响应解析函数parseAssessmentResponse对大小写、空白、前缀宽容(" Run "也能识别),其完整边界用例见单元测试 watch.test.ts。
第四步:交给执行器跑真实浏览器回归
一旦决策为run,Watch 把上下文交给Executor(packages/supervisor/src/executor.ts):智能体生成测试计划,Playwright 驱动真实浏览器(可选注入你系统浏览器的真实 cookie,模拟已登录用户)逐步执行。执行过程中RunUpdate事件持续把步骤状态推给界面;全部完成后:
- 发出
RunCompleted,终端更新运行次数与通过/失败结果,并可选择系统通知提醒(sendWatchNotification) - **保存"已测试指纹"**到项目的
.expect状态目录(该目录自带.gitignore,见 git/git.ts)——下次轮询时指纹不变就绝不会再重复测试,避免浪费
容错设计:背压回退与"宁多测,勿漏测"
Watch 的失败哲学是安全失败(fail-safe):误触发一次测试只是浪费几分钟,而漏测才是真风险。
- 评估失败计数:AI 返回了无法解析的内容时,失败计数 +1;同一个指纹下连续失败达到2 次(
ASSESSMENT_BACKOFF_LIMIT)后,放弃调用 AI,直接默认run,避免反复烧 token - 智能体异常兜底:流中断、会话创建失败、未登录、用量超限等所有 agent 错误(
handleAgentAssessmentError)一律降级为run并记录警告日志 - 执行错误不中断循环:单次执行出错只发
Error事件,轮询循环继续,等下一轮变更
事件驱动的终端界面:你看到的是什么
CLI 侧的 Watch 界面(watch-screen.tsx)是一个纯事件消费者:把WatchEvent映射为六种 UI 阶段(polling / settling / assessing / running / idle / error),并维护运行计数、最近结果(passed/failed)与耗时计时。也就是说,你在终端看到的每次"转圈",背后都精确对应上述流水线中的某一个阶段。
快速上手:三步开启 Watch 模式
- 确认环境:处于一个 git 仓库内(非仓库目录会回退为按当前目录工作)
- 启动监控:运行
expect watch,可选加上-m "重点检查登录流程"或-t unstaged - 安心开发:改代码 → 停手 3 秒 → 自动评估 → 该测就测;指纹没变则永不重复测试
常用参数(见 README.md 的 Options 表):-a <agent>指定智能体、--browser-mode headless无头运行、-u <url>直接指定开发服务器地址。
关键源码路径速查
| 模块 | 路径 | 职责 |
|---|---|---|
| Watch 主循环 | packages/supervisor/src/watch.ts | 轮询、稳定化、双层决策、背压 |
| 指纹与 git 状态 | packages/supervisor/src/git/git.ts | SHA-256 指纹、已测试指纹持久化 |
| 分类提示词 | packages/shared/src/prompts.ts | run/skip 单字决策提示词 |
| 浏览器执行器 | packages/supervisor/src/executor.ts | 测试计划生成与 Playwright 执行 |
| CLI 命令入口 | apps/cli/src/commands/watch.ts | 参数解析与界面挂载 |
| 终端 Watch 界面 | apps/cli/src/components/screens/watch-screen.tsx | 事件 → 阶段映射、通知 |
一句话总结:Expect Watch 模式 =2 秒指纹轮询 + 3 秒稳定化防抖 + 确定性过滤与 AI 分类的双层决策 + 真实浏览器回归执行,并用"宁多测、勿漏测"的容错策略保证监控永不失手。
【免费下载链接】expectExpect tests your agent's code in a real browser项目地址: https://gitcode.com/gh_mirrors/expect6/expect
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考