测试指南(Testing Guide)
June 19, 2026 · View on GitHub
面向所有协作者(含非专业用户)的测试地图:测什么、怎么一键跑、新功能往哪加测试。 主入口:CLAUDE.md §测试分层与 AI 选命令规则。 配套:update-rules.md(验收清单)、testing-rebuild-handoff.md(浏览器测试体系修复交接)、CATALOG.md(文档索引)。
URDF Studio 采用大厂常见的测试金字塔分层:底层多而快、顶层少而慢。理解这三层,就知道任何改动该跑哪个命令、该补哪种测试。
╱ L3 语料/真值回归 ╲ 少、最慢、最接近真实(真实机器人语料 + golden 对比)
╱────────────────────╲
╱ L2 浏览器 E2E 测试 ╲ 中等数量、慢(真启浏览器,按功能点过用户路径)
╱──────────────────────────╲
╱ L1 单元测试(536+) ╲ 最多、最快(纯 Node,毫秒级,逻辑/边界)
────────────────────────────────
L1 · 单元测试(最常用)
- 是什么:纯 Node(
node:test+node:assert/strict)测试,不启浏览器、毫秒级。覆盖解析器、store、hooks、工具函数的逻辑与边界。 - 放在哪:紧挨源码,
src/**/*.test.ts(约 538 个)。新写的工具/纯逻辑就在它旁边建xxx.test.ts。 - 怎么跑:
| 目的 | 命令 |
|---|---|
| 默认快速冒烟(CI 用) | npm test |
| 跑全部单元测试 | npm run test:unit:all |
| 只跑某个文件 | npm run test:unit -- src/core/robot/builders.test.ts |
| 看有哪些 suite / 各有多少文件 | npm run test:unit:list |
- runner:
scripts/test/runner/run-node-tests.mjs(管理 fast / src / all 等 suite)。
L2 · 浏览器端到端(E2E)测试
- 是什么:用 Puppeteer 真启一个 headless 浏览器,按功能走一遍用户路径(导入模型 → 操作 → 断言)。这是"碰撞编辑、测量、拼接、导入导出、AI、主题、显示开关…"这类用户可见功能的测试层。
- 放在哪:
scripts/test/browser/test_*.mjs,每个文件对应一个功能;当前覆盖 30 个浏览器用例(含 7 个 MuJoCo/MJCF 专项)。共用地基:scripts/test/helpers/browser-helpers.mjs—— 启服务器/浏览器、文件上传、加载触发、稳定化。scripts/test/helpers/assertions.mjs——createTestSuite/assert*/printSummary。scripts/test/browser/helpers/<格式>-helpers.mjs—— 各格式(urdf/mjcf/sdf/usd/xacro)的importModel,封装"上传 + 选中 + 加载"。
- 怎么跑:
| 目的 | 命令 |
|---|---|
| 单个功能 | npm run test:browser:theme(或 :mujoco-import / :measure / :collision-opt / :urdf-import …) |
| 全部浏览器测试 | npm run test:browser:all(内部调用 run-all --browser-only,自动发现全部 test:browser:*) |
| 看有哪些 | node scripts/test/runner/run-all.mjs --list |
- 关键机制:测试通过 URL 上的
?regressionDebug=1暴露window.__URDF_STUDIO_DEBUG__调试接口(默认关闭,见 CLAUDE.md 红线)。helper 会自动加这个参数。 - 跑完务必清理(CLAUDE.md 红线):
node test/usd-viewer/scripts/cleanup-headless.cjs。
L3 · 语料 / 真值回归(最慢、最真实)
- 是什么:拿真实机器人语料(MuJoCo menagerie、Unitree、Gazebo)批量导入/导出,与预先生成的 golden/truth 数据对比。还有性能 benchmark。
- 怎么跑:
npm run test:fixtures(聚合)或单项test:fixtures:*。需要test/下的大型语料。 - 语料从哪来:
npm run test:setup(克隆全部)或单项test:setup:*。脚本在scripts/test/setup/,幂等(已存在则跳过)。
"一口气全跑完":统一入口 run-all
scripts/test/runner/run-all.mjs 把三层串起来跑,某个失败不会中断全局,最后打印一张通过/失败总表并写入 tmp/regression/run-all-summary.json。它会先起一个共享 dev server,让所有浏览器测试复用、不必各自冷启动 Vite。
npm run test:all # 便捷别名 = run-all.mjs(单元 + 全部浏览器)
node scripts/test/runner/run-all.mjs # 单元 + 全部浏览器(默认)
node scripts/test/runner/run-all.mjs --unit-only # 只跑单元
node scripts/test/runner/run-all.mjs --browser-only --filter export # 只跑名字含 export 的浏览器测试
node scripts/test/runner/run-all.mjs --fixtures # 额外带上 L3 语料回归
node scripts/test/runner/run-all.mjs --headed # 显示浏览器窗口(调试用)
node scripts/test/runner/run-all.mjs --list # 只列出将要跑的阶段
浏览器层整体较慢(每个用例都要导入模型、构建 3D 场景)。日常开发用
npm test(L1)即可;提交大改动或发版前再run-all。
现成的快速/完整流水线(不含浏览器层):npm run verify:fast(格式+lint+typecheck:quality+L1+构建)、npm run verify:full(再加 L3 fixtures)。
新增功能时,该往哪一层加测试?
- 纯逻辑 / 工具函数 / store action / 解析器:加 L1 单元测试(
src/**/*.test.ts),最优先,便宜又快。 - 用户能在界面上操作的功能(点按钮、拖拽、切换、导入导出):加 L2 浏览器测试。
- 在
scripts/test/browser/仿照同类test_*.mjs新建一个文件; - 用
createSession起会话、对应格式 helper 的importModel导入模型、store.*操作、assert*断言、printSummary收尾; - 在
package.json加一条test:browser:<name>,run-all.mjs会自动发现它(无需改 run-all)。
- 在
- 涉及真实模型解析/导出正确性:加 L3 语料回归(
scripts/test/truth/)。
判断原则:能用 L1 覆盖的逻辑就别用 L2(快、稳、便宜);只有"必须真跑浏览器才能验证"的交互/渲染才上 L2。
读懂结果
- 单元测试:末尾
pass / fail计数,退出码非 0 即失败。 - 浏览器测试:每条断言一行
[suite:<名字>] ✓/✗ ...,结尾[summary:<名字>] passed/failed。 - run-all:总表 +
tmp/regression/run-all-summary.json;每个浏览器用例还各自写tmp/regression/<name>_results.json。