有一类测试失败最隐蔽:终端显示绿色,产品页面也能打开,但教师工作台里突然多了测试学生、孤儿作业或异常掌握度。脚本证明了自己能写,却破坏了下一次演示要使用的事实。
TeachFlow 曾经让多条回归复用共享开发数据库,并依赖 seed 和清理顺序恢复现场。随着课堂、作业、Evidence、Current、Outbox 和 Neo4j 投影越来越多,这个假设终于失效。
最小失败链
预期行为:回归构造测试事实、完成断言、退出后不影响共享演示数据。
实际风险:脚本中途异常,或者清理遗漏 append-only 子记录;下次 seed 只补缺失数据,不会知道哪些残留属于失败测试;Golden Demo 又把共享库当作权威样本,最终出现“测试通过、演示不可信”。
受影响的不只是页面整洁,而是三类结论:
- 班级人数和证据覆盖不再可信;
- 恢复演练可能在外键孤儿上失败;
- 不同任务并行时会互相改变基线。
根因不是“清理脚本少删了一张表”
增加更多 DELETE 可以暂时止血,却没有恢复不变量。真正被破坏的合同是:普通回归不应该拥有共享演示事实的生命周期。
Playwright 官方文档把测试隔离定义为每个测试在独立 clean-slate 环境运行,从而避免失败传递并提高可复现性。浏览器上下文解决 cookies、storage 等客户端状态;TeachFlow 需要把同一原则延伸到数据库、图库、端口和上传资产。
修复落在最早的权威层
常规 classroom-learning-loop:check 现在由包装器先创建一次性 PostgreSQL 和一次性 Neo4j,执行完整 migration,分配动态空闲端口,再启动当前 production build。测试完成或子脚本异常退出时,父进程负责销毁临时数据环境。
这个改动恢复了几个关键不变量:
- 普通回归写入只能存在于任务拥有的数据库;
- 并行检查不争用固定端口;
- 测试夹具、图片字节和投影随临时环境一起消失;
- 脚本不能依赖前一个脚本留下的课程或 Session;
- Smoke 必须显式导入自己要检查的内容。
Golden 数据没有被一刀切删除
隔离并不意味着所有测试都要使用同一种数据库。
demo:golden-check 的职责是证明共享 seed 之后那条固定演示链仍然成立,因此它必须在明确的共享演示库上运行;PostgreSQL 恢复演练也必须针对当前发布源库做 dump/restore。普通回归则不允许借用这些状态。
这是一条容易忽略的经验:测试隔离不是把所有环境都临时化,而是让每个入口只拥有自己职责所需的状态。
修复之后暴露出的真实问题
当回归不再依赖共享步骤顺序,一些之前被遮住的错误反而出现了:
- 某些脚本没有准备自己的 Publication;
- Smoke 在空库里绿色,却实际上少测了课程数据;
- replica 清理顺序留下孤儿外键,直到
pg_restore才失败; - 大分页游标把 PostgreSQL 微秒截成 JavaScript 毫秒,10,005 道题偶发只返回 10,004 道。
这说明隔离的价值不只是“保持干净”,它还迫使测试声明自己的前置条件。
为什么不能只看 HTTP 200
一个健康接口返回 200,只能证明当前请求成功。它不能证明数据库没有残留、浏览器完成真实动作、跨班授权被拒绝、备份可以恢复,或异常退出后资源被清理。TeachFlow 因此把类型、数据库合同、production 浏览器、恢复和 Golden seed 拆成不同证据层。
当前证据边界
项目记录表明隔离回归曾覆盖双班 Publication/Session/Board/Assignment/Evidence/Materialization 和浏览器恢复;本文固定快照未附带新的完整发布门禁,也没有当前机器的容器资源审计。本文可以说明修复机制与历史发现,不能把历史 21 步回执描述成当前运行结果,更不能推出学校生产并发。
常见问题与设计边界
为什么不用事务回滚代替临时数据库?
浏览器、多个 HTTP 请求、Worker、Neo4j 与子进程跨越单一事务边界;临时数据库为整条链提供更清晰的生命周期。
每次建库会不会太慢?
会增加成本,所以快速类型/lint 与全栈隔离检查分层运行。速度不能靠复用不可信状态换取。
如何防止异常退出留下资源?
父进程持有资源清单,在 finally 和信号处理路径清理;最终仍要审计实际运行集合,不能只相信退出日志。
参考资料
- Playwright:Test Isolation
- PostgreSQL:ROLLBACK
- TeachFlow
scripts/classroom-runtime-release-candidate.mjs、scripts/lesson-publication-check-isolated.mjs、项目亮点与演进.md
结论
测试污染不是“测试数据不好看”,而是测试越过了自己的状态所有权。TeachFlow 的 durable fix 不是继续加清理分支,而是让普通回归从创建到销毁都拥有独立环境,同时保留 Golden 和恢复演练各自真实职责。绿色结果只有不改变被测基线,才值得信任。