TESTING —— agentaily monorepo(测试真相源)
这个仓怎么测:分层、框架选型、在哪道闸拦回归。是测试策略的单一出处 —— 改选型 / 护栏 / 分层,同一次改动更新本文件(文档与代码同步)。
- 真相源同侪:
SPEC.md(规格)·ARCHITECTURE.md(架构)·ROADMAP.md(能力进度)· 本文件(测试)。 - 一句话:vitest 4 一把梭,靠根
vitest.config.ts的projects把不同环境的包编排到一起跑(纯逻辑包跑 Node,带 React/DOM 的包跑各自的 jsdom 配置)。 - 双循环方法论配套:本仓用「BDD 外环 + TDD 内环 + sub-agent 分工」组织开发 —— 角色边界 / 交接协议 / 五条铁律 / 编排策略见
.claude/agents/README.md。下面这套分层正是implementer(内环,红→绿→重构)/outer-tester(外环,验收)把spec-architect写的features/行为契约 realize 出来、并在护栏上拦回归的方式。
测试分层(多而快在下,少而真在上)
| 层 | 海拔 / 环 | 谁写 | 框架 / 环境 | 测什么 | 位置 | 跑在哪 |
|---|---|---|---|---|---|---|
| unit 纯逻辑单测 | 内环(TDD,最多最快) | implementer | vitest · node 环境 | aml / 数据引擎 / 迁移 / agent loop / 市场数据层 / VFS 组装等纯函数(behavior-styled AAA,不为每断言写 Gherkin) | llm presets eval backend db aml agent apps/web apps/publish 下的 *.test.ts | pnpm test + CI |
| 组件单测 | 内环 | implementer | vitest · jsdom + React(@testing-library) | design-system 组件渲染 / 交互 | design-system/**(自带 design-system/vitest.config.js) | pnpm test + CI |
| BDD 行为契约 | 横切真相源 | spec-architect 写 / 两环对着它 | Gherkin .feature | 系统该做什么(business-readable,唯一真相源) | 它所描述的包根 features/(如 apps/web/features/、apps/website/features/) | (契约本身;被外环测试 realize) |
| integration 集成 | 外环(BDD 验收) | outer-tester | vitest · jsdom + React + @amiceli/vitest-cucumber | 组件 / 跨模块协作,realize features/ 场景 | 相关 DOM 包 tests/integration/*(范式:apps/website/tests/integration/) | pnpm test + CI |
| 外环(最高最真) | outer-tester | (当前无 —— 未引入 Playwright/Cypress) | 用户真实走一遍 | — | — |
编排机制(头号坑): 一把
vitest run默认会把 design-system 的 jsdom 测试也扫进 Node 环境而炸。所以根vitest.config.ts用test.projects:① 一个内联nodeproject 圈定纯逻辑包;② 把design-system、apps/website各自的 config 文件作为独立 project 引入(它们各带 jsdom + react)。新增一个需要 DOM 的包 → 给它写自己的vitest.config.js并在根projects里引一行;新增一个纯逻辑包 → 把它的 glob 加进根nodeproject 的include。 漏了这步,它的测试根本不会被跑(静默漏测)。
框架选型(为什么这么选)
- 单测 / 集成 → vitest 4:与 Vite 同源、ESM/TS 原生、
projects能在一次进程里编排多环境(Node + 多个 jsdom),正好贴合本仓「纯逻辑包 + React 包」混合的形态。turbo 负责build/typecheck的跨包编排,测试则统一交给根 vitest 一次跑完。 - 组件 / 官网 → jsdom +
@testing-library/react:测「用户看到/能做什么」而非实现细节;DOM 在 jsdom 里够用、比真浏览器快得多。 - BDD 契约 → Gherkin
features/*.feature:business-readable、行为可执行,作为「系统该做什么」的契约真相源,由spec-architect写、内外环都对着它。放在它所描述的那个包根的features/(apps/website/features/switch-locale.feature是已 realize 的活例;apps/web/features/已种入build-app/market-fork,待outer-testerrealize)。 - e2e:暂未引入(没有 Playwright/Cypress)。等出现「跨页面真实链路」必须验时再加一层,别提前上重器。
护栏(质量门)
| 阶段 | 闸 | 拦什么 |
|---|---|---|
| 写时 | plan + 先写失败测试 | 方向 / 实现错 |
| 提交 / 推送 | (当前未配 git hooks) | —— 暂无本地预提交闸,质量全压在 CI |
PR / push main | .github/workflows/ci.yml:pnpm typecheck → pnpm test → pnpm build(Node 24 + pnpm 11.7,--frozen-lockfile) | 类型错 / 测试红 / 构建坏 |
| 合并后 | deploy.yml(agentaily)+ deploy-website.yml(官网)CD | —— |
没有 lefthook/husky:push 前自己本地把下面那条 done 命令跑绿,别依赖 hook 兜底。CI 三步任一红都会拦住 PR / 阻断部署。
本地一条命令验完(done 的定义)
bash
pnpm typecheck && pnpm test && pnpm buildpnpm build = turbo run build(各包各自构建;app 前端是零构建 VFS,沙箱直跑,无 studio 汇入步)—— build 绿即代表各包都编译过、apps/web 制品已产出。
约定 / 坑(this repo)
- 测试文件命名
*.test.ts(x):纯逻辑包用*.test.ts(被根nodeproject 的 glob 捞);DOM 包按各自 config 的include(如apps/website是tests/**/*.{test,spec}.*)。 - 新包接测试 = 改两处之一(见上「编排机制」坑):纯逻辑 → 根
node.include加 glob;带 DOM → 写包内vitest.config.js+ 根projects引一行。 apps/web(主 SPA)的纯逻辑已纳入编排(apps/web/**/*.test.ts在根nodeproject,如 VFS 内联预览组装器);但它还没有 DOM / 集成测试 ——core/*(apiClient/auth/marketStore/conversationStore)与/build、市场 UI 的用户可见行为是已知缺口。补 DOM / 集成测时给apps/web建vitest.config.js(jsdom + react)并入根projects,再用@amiceli/vitest-cucumberrealizeapps/web/features/*.feature(本仓已种入build-app/market-fork两条行为契约,待outer-testerrealize)。- 推进模型:改
vitest.config.ts/pnpm-lock.yaml等共享文件走 worktree + PR —— 在自己的 worktree 里改、窄暂存(别git add -A)、开 PR 合并,见CLAUDE.md的「🌳 推进模型」。