# 测试指南(Testing Guide) > 面向所有协作者(含非专业用户)的测试地图:测什么、怎么一键跑、新功能往哪加测试。 > 最后更新:2026-07-09。 > 主入口:[CLAUDE.md](../CLAUDE.md) §测试分层与 AI 选命令规则。 > 配套:[update-rules.md](update-rules.md)(验收清单)、[CATALOG.md](CATALOG.md)(文档索引)。 URDF Studio 采用大厂常见的**测试金字塔**分层:底层多而快、顶层少而慢。理解这三层,就知道任何改动该跑哪个命令、该补哪种测试。 ``` ╱ L3 语料/真值回归 ╲ 少、最慢、最接近真实(真实机器人语料 + golden 对比) ╱────────────────────╲ ╱ L2 浏览器 E2E 测试 ╲ 中等数量、慢(真启浏览器,按功能点过用户路径) ╱──────────────────────────╲ ╱ L1 单元测试(599) ╲ 最多、最快(纯 Node,毫秒级,逻辑/边界) ──────────────────────────────── ``` --- ## L1 · 单元测试(最常用) - **是什么**:纯 Node(`node:test` + `node:assert/strict`)测试,不启浏览器、毫秒级。覆盖解析器、store、hooks、工具函数的逻辑与边界。 - **放在哪**:紧挨源码,`src/**/*.test.*` / `src/**/*.spec.*`(runner 当前管理 `src` 邻近测试 593 个,全量 Node 测试 599 个)。新写的工具/纯逻辑就在它旁边建 `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)。 ### Assembly 单真源回归矩阵 Assembly/workspace 改动至少覆盖以下不变量: - 空白与五种直接打开格式都是 `1 component + 0 bridges`;再次打开会替换,preview 不写 store,Add 只追加且同一 source 可多实例 - component 内 ID 为 source-local;重复 link/joint/tendon 名通过 `EntityRef` 正确路由,projection global ID 只经显式双向映射解析 - 单/多 component 对 topology、material、inertial、collision、IK、tendon 和 joint motion 使用同一 mutation API;高频 motion flush 只产生一条 workspace history - `direct-component` / `assembled-scene` 切换前后 canonical snapshot 不变,selection、transform controls 和 component root placement 往返一致 - 单组件 source-preserving export、多组件 merged/bridge/断联 bundle、library 独立导出和 USD cache 隔离均从 canonical workspace 派生 - `.usp` 仅验证严格 `3.0` single/multi roundtrip、history、active component、source drafts 与 USD cache;旧版本或损坏 manifest 必须 fail-fast,不增加迁移测试 ## L2 · 浏览器端到端(E2E)测试 - **是什么**:用 Puppeteer 真启一个 headless 浏览器,按**功能**走一遍用户路径(导入模型 → 操作 → 断言)。这是"碰撞编辑、测量、拼接、导入导出、AI、主题、显示开关…"这类用户可见功能的测试层。 - **放在哪**:`scripts/test/browser/test_*.mjs`,每个文件对应一个功能;当前有 41 个浏览器测试脚本(含 URDF / MJCF / SDF / USD / Xacro、Editor deep、source apply、AI、主题、测量、碰撞、组装导出等)。共用地基: - `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` / `:usd-import` / `:xacro-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。 ```bash 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)。 --- ## 新增功能时,该往哪一层加测试? 1. **纯逻辑 / 工具函数 / store action / 解析器**:加 **L1** 单元测试(`src/**/*.test.ts`),最优先,便宜又快。 2. **用户能在界面上操作的功能**(点按钮、拖拽、切换、导入导出):加 **L2** 浏览器测试。 - 在 `scripts/test/browser/` 仿照同类 `test_*.mjs` 新建一个文件; - 用 `createSession` 起会话、对应格式 helper 的 `importModel` 导入模型、`store.*` 操作、`assert*` 断言、`printSummary` 收尾; - 在 `package.json` 加一条 `test:browser:`,`run-all.mjs` 会自动发现它(无需改 run-all)。 3. **涉及真实模型解析/导出正确性**:加 **L3** 语料回归(`scripts/test/truth/`)。 判断原则:**能用 L1 覆盖的逻辑就别用 L2**(快、稳、便宜);只有"必须真跑浏览器才能验证"的交互/渲染才上 L2。 ## 读懂结果 - 单元测试:末尾 `pass / fail` 计数,退出码非 0 即失败。 - 浏览器测试:每条断言一行 `[suite:<名字>] ✓/✗ ...`,结尾 `[summary:<名字>] passed/failed`。 - run-all:总表 + `tmp/regression/run-all-summary.json`;每个浏览器用例还各自写 `tmp/regression/_results.json`。