Cabinet 测试体系实战:Playwright E2E 与 Fake Agent CLI 搭建指南
【免费下载链接】cabinetAI-first knowledge base and startup OS项目地址: https://gitcode.com/gh_mirrors/cabinet3/cabinet
Cabinet 是一款 AI-first 的自托管知识库与创业操作系统(startup OS),而支撑它稳定运行的,是一套以Playwright E2E 端到端测试为核心的测试体系。这套体系最巧妙的设计,是用脚本化的 Fake Agent CLI替代真实 AI 模型——测试无需 API Key、无需联网,却能确定性地跑穿整个产品链路。本文用一篇完整指南,带你从零看懂并亲手跑通这套 AI 智能体应用测试方案 🧪
Cabinet 本身要对接 Slack、Discord、Telegram 等大量外部工具和 AI 命令行(claude、codex、gemini 等)。正因为外部依赖这么多,它的测试体系把"隔离不确定性"做到了极致:
测试体系全景:3 层防线如何守护 AI 知识库
Cabinet 的测试分为三层,各司其职:
| 层级 | 工具 | 位置 | 职责 |
|---|---|---|---|
| 单元测试 | Node 内置 test runner(tsx --test) | test/ 目录,350+ 个用例 | 纯函数、解析器、配置逻辑 |
| E2E 端到端测试 | Playwright | e2e/ 目录,5 个 spec 文件 | 拉起真实应用,跑穿核心产品链路 |
| 夜间 Agent 审计 | 真实 CLI 契约测试 | CI 定时任务(设计见 CI 测试管线设计文档) | 用真实模型验证输出格式兼容性 |
单元测试由 scripts/run-unit-tests.mjs 统一调度;E2E 层则由本文的主角——harness + Fake Agent CLI——撑起。核心思想只有一句话:状态必须被注入,而不是被环境碰巧带上。
Playwright E2E 配置详解:为什么故意不用 webServer
打开 playwright.config.ts(仅 23 行),你会发现一个反直觉的决定:没有配置webServer。
testDir: "./e2e" fullyParallel: true timeout: 120_000 // 拉起真实应用比组件测试慢 trace: "retain-on-failure" screenshot: "only-on-failure"原因写在文件头部的注释里:如果由 Playwright 统一拉起一个共享服务,所有测试就会共享同一份可变状态——而这正是本测试套件要彻底消除的"环境状态污染"问题。取而代之,每个 spec 文件自己启动一套隔离的应用实例(详见下一节),测试之间互不干扰,fullyParallel可以放心全开。
两个 CI 友好细节也值得抄进你自己的项目 ⚡
forbidOnly: !!process.env.CI:防止调试时忘了删.only就被合入retries: process.env.CI ? 1 : 0:CI 上重试一次,抵消偶发抖动
一键拉起真实应用:harness 的 4 个隔离技巧
bootCabinet() 是整个 E2E 体系的引擎,它在临时目录里拉起一个完全真实的Next.js 应用 + 守护进程。四个隔离技巧值得逐一学习:
- 临时数据目录:注入
CABINET_DATA_DIR环境变量指向临时目录,并拷入种子 fixture(根清单 + 一个可运行的editor智能体 + 工作区配置,位于 test/support/fixtures/e2e-cabinet/)。注意数据目录是模块级常量、导入时冻结,所以必须在进程启动时注入,事后无法更改。 - 临时端口:
CABINET_DAEMON_PORT指向随机空闲端口,天然支持并行。 - 假 HOME 遮蔽真实 CLI:这是最容易被忽略的一步。Cabinet 解析 CLI 的路径规则会把
~/.local/bin排在$PATH之前——仅仅修改PATH根本盖不住开发者本机装好的真实 Claude。harness 因此为子进程造了一个临时 HOME,把假 CLI 装进它的.local/bin,确保测试绝不悄悄调用真实模型烧钱。 - 轮询就绪而非 sleep:启动后对
/health端点轮询,替代"睡几秒赌它起来了"的经典抖动源。
启动失败时,waitForOk() 会把应用和守护进程最后 25 行日志附在错误里——排障体验直接拉满。
Fake Agent CLI 搭建指南:让 AI 输出变得可预测
如果 harness 解决"环境隔离",createFakeAgentCli() 就解决"行为确定性"。它生成一个真实可执行的 Node 脚本冒充claude命令,被产品的真实适配代码原样 spawn——真实的 argv 构造、真实的流式解析、真实的退出码处理全部走一遍,唯独没有网络调用和模型意见。
它有两个核心能力,正是"测试接缝"而非"简单桩"的关键:
- 脚本化(Scripted):每次调用按顺序消费一步程序。Cabinet 一次对话会多次 spawn CLI(重试、追问、会话恢复),一次性回放假数据的桩无法区分"第 1 轮"和"第 3 轮",而脚本化的假 CLI 可以——甚至能故意挂起(
hang)来测试用户的"停止"按钮。 - 记录(Recording):每次调用把 argv、stdin、cwd 追加写入 JSONL 日志。这是断言"契约另一半"的唯一途径——Cabinet请求了什么:
--resume是否带了上一轮捕获的会话 ID?提示词是否走 stdin 而非 argv?工作目录是否落在知识库内部?
两个辅助函数让编写测试用例几乎零成本:
| 辅助函数 | 作用 |
|---|---|
| claudeReply() | 一步成功的流式回复,自动附带协议要求的 ```cabinet 收尾块 |
| claudeFailure() | 一步失败,通过 stderr 文本触发真实错误分类(登录过期、限流等) |
选 Node 而非 shell 脚本也藏着一个工程细节:Cabinet 的停止逻辑向进程组发 SIGTERM,shell 里sleep的子进程收不到信号会"活过测试",而 Node 进程默认会被终止——这类决策只有踩过坑才会懂。
5 个 E2E 用例:从对话到产物,跑穿核心链路
e2e/ 目录下 5 个 spec 文件,恰好覆盖智能体产品最要命的五条链路:
| 用例文件 | 验证内容 |
|---|---|
| agent-conversation.spec.ts | 🎯 曳光弹:发消息 → 假 CLI 流式回复 → 页面渲染,同时断言 CLI 调用参数符合打印模式契约 |
| agent-failure.spec.ts | 登录过期、限流等失败被分类并给出修复提示,而不是裸报错;用户点"停止"的行为 |
| agent-turns.spec.ts | 多轮对话:第 2 轮必须携带--resume <会话ID>,否则上下文悄悄丢失 |
| agent-triggers.spec.ts | 定时任务触发 + 技能文件挂载(两者都不出现在界面上,只有调用记录能暴露回归) |
| agent-outputs.spec.ts | 产物文件真实落盘、待审批动作必须人工批准才派发——智能体系统的"爆炸半径"边界 |
断言工具也很克制:cabinet-api.ts 提供基于fetch的类型化封装(startConversation、waitForStatus 等),测试既能无头驱动 API,也能在需要时才打开浏览器断言 DOM。
新手上手:3 步跑通你的第一条 E2E 测试
git clone https://gitcode.com/gh_mirrors/cabinet3/cabinet cd cabinet && npm install npm run test:e2e就这么简单。每个 spec 会通过 harness 自建隔离环境,你不需要预先配置任何密钥或环境变量。想同时跑单元测试,执行npm run test即可。建议打开 CI 测试管线设计文档 对照阅读,理解"曳光弹"(tracer bullet)这条核心链路的取舍逻辑。
进阶:让 E2E 测试成为 CI 的合并闸门
这套设计天然适合卡合并:因为没有 API Key、没有网络,e2e 任务可以安全地跑在 fork PR 上,且失败即阻断。设计文档给出的 CI 拓扑是:
- 每 PR(阻断式):构建 → 单测转真闸门 → e2e(复用构建产物,失败时上传 trace 和截图)
- 每夜(只报告不阻断):装真实 claude/codex/gemini CLI 跑契约测试,兜住"模型厂商悄悄改了输出格式"这类单测看不到的风险
写在最后
Cabinet 测试体系给 AI 应用开发者留下了三个可复用的模式 📌
- 注入而非依赖环境:数据目录、端口、HOME 全部在进程启动时注入,状态干净
- 假二进制替代假 Mock:让产品真实代码路径原样执行,只替换最末端的外部进程
- 既断言输出、也断言请求:记录每次调用,契约的两半都要验证
核心资料索引:playwright.config.ts · test/support/harness.ts · test/support/fake-agent-cli.ts · test/support/cabinet-api.ts · e2e/
【免费下载链接】cabinetAI-first knowledge base and startup OS项目地址: https://gitcode.com/gh_mirrors/cabinet3/cabinet
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考