Files
aibis-dream/Docs/UnityTestingGuidelines.md
T

9.3 KiB

AIBIS Dream Unity Test 规范

1. 目标

Unity Test 用来快速发现问题、保护已经确认的系统契约和高风险回归。可以为开发中的功能编写阶段性自动测试,但它们必须与长期测试隔离,并在达到删除或转正条件时处理。代码修改不要求机械地新增测试。

测试套件应满足三个目标:

  1. 失败时能指出一个明确的玩家行为、数据兼容性或系统边界问题。
  2. 重构实现但不改变行为时,大部分测试无需修改。
  3. 开发者能在合理时间内运行完整 EditMode 套件,并愿意经常运行它。

2. 何时应该添加测试

满足以下任一项时,适合添加或扩展长期 Unity Test:

  • 已验收、会长期存在的业务规则或公共 API 契约。
  • 存档格式、Addressable 地址、资源导入等一旦破坏就会造成严重后果的边界。
  • 曾经发生且很可能复发的缺陷;测试应复现用户可观察的错误。
  • 复杂纯逻辑存在清晰输入输出,测试比场景手测更快、更可靠。
  • 必须由 Unity 生命周期、Timeline、协程或真实组件组合才能验证的关键集成。

以下测试不应直接进入长期测试套件:

  • 功能仍处于方案探索、中间阶段或尚未验收;如确有自动验证价值,应进入临时测试目录。
  • 仅调整编辑器窗口布局、按钮位置、颜色、动效参数或调试展示。
  • 私有方法、字段赋值、属性包装器等实现细节。
  • 同一行为已经由更低层、更快的测试覆盖。
  • 为每个枚举值、边界值机械复制一个测试,而这些输入属于同一等价类。
  • 资源内容完整性扫描。此类检查优先放到专用 Validator、导入检查或发布前检查中。
  • 只服务开发期的临时工具、测试存档、实验性入口;此类测试应随对应功能一起删除,或在工具转正时重新评审。

代码发生变化本身不是添加测试的理由。没有满足上述条件时,可以不改测试。

2.1 临时自动测试

开发中的复杂功能可以使用临时自动测试辅助迭代。临时并不代表低质量:它们仍需可重复运行、可靠清理状态,并选择合适的测试层级。

临时测试必须满足:

  • 放在 Assets/Tests/_Temporary/<Feature>/,不能混入长期领域套件。
  • 目录内包含简短说明,写明用途、负责人、创建日期以及删除或转正条件。
  • 测试名称或断言可以面向当前阶段,但不得迫使生产代码暴露只为测试服务的公共 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/<System>/EditMode/PlayMode/
  • 临时测试统一从 Assets/Tests/_Temporary/<Feature>/ 开始组织。默认程序集的临时 EditMode 测试放入其 Editor/ 子目录;独立程序集测试可在功能目录中建立最小临时 asmdef。
  • 测试 asmdef 必须包含 UNITY_INCLUDE_TESTSTestAssemblies,并且只引用被测系统真正需要的程序集。
  • 不把测试脚本混放在业务 Editor/ 工具、运行时代码或功能目录中。
  • 测试生成资源使用唯一临时目录,并在 TearDown 中删除;生成物和 .meta 不得提交。

当前保留的测试职责如下:

套件 职责
Assets/Editor/Tests 音频/存档兼容、本地化、Yarn 元数据、设置值、关键 Prefab 契约
FrameAnimationCoreTests 图模型、解析拒绝、播放会话、结束行为、请求句柄
FrameAnimationImportTests Aseprite 解析和一次完整刷新链路
FrameAnimationPlayerTests 运行时组件、请求替换、恢复和生命周期
FrameAnimationTimelinePlayModeTests Timeline 接管、Seek 和释放播放器

5. 单个测试的设计

命名

使用 Subject_State_Outcome,例如:

[Test]
public void PlayRequest_MissingPlayable_KeepsActiveRequest()

名称描述行为,不描述内部调用顺序。

结构

每个测试保持清晰的 Arrange / Act / Assert 三段。一个测试可以有多个断言,但这些断言必须共同说明一个失败原因。

优先断言:

  • 玩家或调用方可观察的最终状态。
  • 稳定的错误码、存档字段、资源地址或跨系统契约。
  • 必须保持的副作用边界,例如失败请求不能替换当前播放。

避免断言:

  • 私有字段的每个中间值。
  • UI 层级中的全部节点和样式。
  • 与结果无关的精确调用次数或实现顺序。
  • 大段序列化快照;只检查兼容性所需字段。

用例合并

同一等价类的输入放在一个测试内循环验证,避免大量 [TestCase] 让测试面板产生虚假的覆盖感。只有不同输入应呈现为独立失败、且失败定位确实有价值时才使用 [TestCase]

不要为了减少文件数,把互不相关的契约塞进同一个测试。合并的是重复路径,不是失败原因。

6. 稳定性与清理

  • 不依赖网络、系统时间、线程调度顺序、随机种子或本机语言环境。
  • 修改全局状态时,必须在 TearDownfinally 中恢复。
  • 创建的 GameObjectScriptableObject、纹理和 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 后运行:

& '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。