# AIBIS Dream Unity Test 规范 ## 1. 目标 Unity Test 用来快速发现问题、保护已经确认的系统契约和高风险回归。可以为开发中的功能编写阶段性自动测试,但它们必须与长期测试隔离,并在达到删除或转正条件时处理。代码修改不要求机械地新增测试。 测试套件应满足三个目标: 1. 失败时能指出一个明确的玩家行为、数据兼容性或系统边界问题。 2. 重构实现但不改变行为时,大部分测试无需修改。 3. 开发者能在合理时间内运行完整 EditMode 套件,并愿意经常运行它。 ## 2. 何时应该添加测试 满足以下任一项时,适合添加或扩展长期 Unity Test: - 已验收、会长期存在的业务规则或公共 API 契约。 - 存档格式、Addressable 地址、资源导入等一旦破坏就会造成严重后果的边界。 - 曾经发生且很可能复发的缺陷;测试应复现用户可观察的错误。 - 复杂纯逻辑存在清晰输入输出,测试比场景手测更快、更可靠。 - 必须由 Unity 生命周期、Timeline、协程或真实组件组合才能验证的关键集成。 以下测试不应直接进入长期测试套件: - 功能仍处于方案探索、中间阶段或尚未验收;如确有自动验证价值,应进入临时测试目录。 - 仅调整编辑器窗口布局、按钮位置、颜色、动效参数或调试展示。 - 私有方法、字段赋值、属性包装器等实现细节。 - 同一行为已经由更低层、更快的测试覆盖。 - 为每个枚举值、边界值机械复制一个测试,而这些输入属于同一等价类。 - 资源内容完整性扫描。此类检查优先放到专用 Validator、导入检查或发布前检查中。 - 只服务开发期的临时工具、测试存档、实验性入口;此类测试应随对应功能一起删除,或在工具转正时重新评审。 代码发生变化本身不是添加测试的理由。没有满足上述条件时,可以不改测试。 ### 2.1 临时自动测试 开发中的复杂功能可以使用临时自动测试辅助迭代。临时并不代表低质量:它们仍需可重复运行、可靠清理状态,并选择合适的测试层级。 临时测试必须满足: - 放在 `Assets/Tests/_Temporary//`,不能混入长期领域套件。 - 目录内包含简短说明,写明用途、负责人、创建日期以及删除或转正条件。 - 测试名称或断言可以面向当前阶段,但不得迫使生产代码暴露只为测试服务的公共 API。 - 功能验收、方案替换、功能放弃或已有长期契约测试覆盖时,删除整个临时目录。 - 若测试发现了需要长期保护的契约,只将最小必要用例整理后迁入正式领域套件,其余删除。 - 评审和功能验收时必须检查 `_Temporary`;不允许没有清理条件的临时测试长期滞留。 ## 3. 测试分层 一个行为默认只选择能够完整覆盖它的最低测试层级,不能按“纯逻辑一份、组件一份、场景再一份”的方式机械叠加。只有上层测试覆盖了下层无法发现的独立风险时,才允许跨层重复。 ### 3.1 EditMode 逻辑契约 首选层级。用于解析、状态机、序列化 DTO、规则计算和错误处理。应尽量不加载场景、不等待帧、不依赖真实时间。 ### 3.2 EditMode 资源集成 只保留少量端到端路径,例如“源文件 -> 导入服务 -> 可播放资源”或“Prefab -> Addressable 地址 -> 运行时组件”。不要逐个测试编辑器操作步骤和窗口状态。 ### 3.3 PlayMode 关键集成 仅当行为依赖 `OnEnable` / `OnDisable`、协程、`PlayableDirector`、物理或真实帧推进时使用。能通过显式求值器或纯逻辑 EditMode 测试验证的行为,不应再写一份 PlayMode 测试。 原则上,每个系统只保留少数关键 PlayMode 主链。新增 PlayMode 测试时,需要说明为什么 EditMode 无法覆盖。 ## 4. 目录与程序集 - 默认 `Assembly-CSharp` 中运行时代码的测试放在 `Assets/Editor/Tests/`。这是因为 asmdef 测试程序集不能直接依赖 Unity 的预定义运行时程序集。 - 已有独立 asmdef 的系统放在 `Assets/Tests//EditMode/` 或 `PlayMode/`。 - 临时测试统一从 `Assets/Tests/_Temporary//` 开始组织。默认程序集的临时 EditMode 测试放入其 `Editor/` 子目录;独立程序集测试可在功能目录中建立最小临时 asmdef。 - 测试 asmdef 必须包含 `UNITY_INCLUDE_TESTS` 和 `TestAssemblies`,并且只引用被测系统真正需要的程序集。 - 不把测试脚本混放在业务 `Editor/` 工具、运行时代码或功能目录中。 - 测试生成资源使用唯一临时目录,并在 `TearDown` 中删除;生成物和 `.meta` 不得提交。 当前保留的测试职责如下: | 套件 | 职责 | | --- | --- | | `Assets/Editor/Tests` | 音频/存档兼容、本地化、Yarn 元数据、设置值、关键 Prefab 契约 | | `FrameAnimationCoreTests` | 图模型、解析拒绝、播放会话、结束行为、请求句柄 | | `FrameAnimationImportTests` | Aseprite 解析和一次完整刷新链路 | | `FrameAnimationPlayerTests` | 运行时组件、请求替换、恢复和生命周期 | | `FrameAnimationTimelinePlayModeTests` | Timeline 接管、Seek 和释放播放器 | ## 5. 单个测试的设计 ### 命名 使用 `Subject_State_Outcome`,例如: ```csharp [Test] public void PlayRequest_MissingPlayable_KeepsActiveRequest() ``` 名称描述行为,不描述内部调用顺序。 ### 结构 每个测试保持清晰的 Arrange / Act / Assert 三段。一个测试可以有多个断言,但这些断言必须共同说明一个失败原因。 优先断言: - 玩家或调用方可观察的最终状态。 - 稳定的错误码、存档字段、资源地址或跨系统契约。 - 必须保持的副作用边界,例如失败请求不能替换当前播放。 避免断言: - 私有字段的每个中间值。 - UI 层级中的全部节点和样式。 - 与结果无关的精确调用次数或实现顺序。 - 大段序列化快照;只检查兼容性所需字段。 ### 用例合并 同一等价类的输入放在一个测试内循环验证,避免大量 `[TestCase]` 让测试面板产生虚假的覆盖感。只有不同输入应呈现为独立失败、且失败定位确实有价值时才使用 `[TestCase]`。 不要为了减少文件数,把互不相关的契约塞进同一个测试。合并的是重复路径,不是失败原因。 ## 6. 稳定性与清理 - 不依赖网络、系统时间、线程调度顺序、随机种子或本机语言环境。 - 修改全局状态时,必须在 `TearDown` 或 `finally` 中恢复。 - 创建的 `GameObject`、`ScriptableObject`、纹理和 Sprite 必须销毁,销毁顺序与创建顺序相反。 - 预期日志使用 `LogAssert.Expect`,不要为了通过测试而全局忽略日志。 - 资产测试使用同步导入和唯一目录;清理逻辑应幂等,不能掩盖原始断言失败。 - 不直接调用 `Resources.Load`;测试中的运行时资源获取同样遵守 `ResourceSystem` 规范。 ## 7. 评审检查清单 新增或修改测试前检查: 1. 这是长期契约测试,还是阶段性的临时测试?目录是否匹配? 2. 临时测试是否写明删除或转正条件? 3. 现有长期测试能否扩展,而不是新建文件或新建程序集? 4. 是否选择了能够覆盖该风险的最低层级? 5. 上下层是否重复验证同一件事,而没有新增独立风险覆盖? 6. 删除或重构实现后,长期测试是否仍然表达同一个需求? 7. 失败信息能否直接告诉开发者哪条契约坏了? 8. 是否创建了临时资产、全局状态或日志,且全部可靠清理? 若测试归属、层级选择或清理条件无法明确回答,不应立即添加测试。 ## 8. Codex 行为约束 Codex 修改本仓库时遵守以下规则: - 可以主动添加有明确反馈价值的 Unity Test,但不因“修改了代码”在每个层级机械补测试。 - 对每个风险选择一个主要测试层级;只有存在不同的集成失败模式时才增加上层测试。 - 未验收、中间态或实验功能的测试必须放在 `_Temporary`,并同时写明删除或转正条件。 - 稳定契约优先扩展现有领域套件;没有新的长期领域边界时,不新建永久测试文件或 asmdef。 - 添加 PlayMode 测试前,确认 EditMode 无法覆盖对应的 Unity 集成风险。 - 如果测试数量或测试代码增幅明显大于生产改动,应先重新审视测试层级和重复覆盖。 - 功能验收时检查 `_Temporary`,将最小必要契约测试转正并删除其余阶段性测试。 - 功能被删除、契约被替换或测试只剩实现细节时,应同步删除测试,不保留“以防万一”的历史测试。 ## 9. 运行方式 关闭正在打开该项目的 Unity Editor 后运行: ```powershell & 'C:\Program Files\Unity\Hub\Editor\2022.3.7f1c1\Editor\Unity.exe' ` -batchmode -projectPath 'D:\UnityProject\aibis-dream' ` -runTests -testPlatform EditMode ` -testResults 'D:\UnityProject\aibis-dream\Temp\EditModeResults.xml' ` -logFile 'D:\UnityProject\aibis-dream\Temp\EditMode.log' ``` 高风险运行时或 Timeline 修改再运行 PlayMode。纯文档、编辑器样式或不影响运行时契约的修改无需机械运行全部 PlayMode。