关于 godot-testing
面向 Godot 4.3+ 开发者的测试技能,讲解如何使用 GUT 或 gdUnit4。内容涵盖框架选择、TDD、常见测试模式,以及本地和 CI 测试运行。
主要功能
- 01GUT 与 gdUnit4 框架选择
- 02RED-GREEN-REFACTOR 测试流程
- 03测试目录与命名规范
- 04命令行及 CI 测试运行
- 05场景、信号、模拟与异步测试
适用场景
- 01为 GDScript 或 C# 项目选择测试框架
- 02按 TDD 流程实现 Godot 功能
- 03在本地或 CI 中运行测试套件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jame581/ godotprompter --skill godot-testing安装后在 Claude Code 中输入/godot-testing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
gdunit4-reference.mdgut-reference.mdreferences/running-tests.mdtdd-workflow.mdtesting-patterns.md详细介绍
Godot 测试
本技能介绍如何在 Godot 4.3+ 项目中使用 GUT(Godot Unit Testing)和 gdUnit4 进行测试驱动开发(TDD)。内容包括框架选择、完整的 RED-GREEN-REFACTOR 示例、测试结构、在 CI 中运行测试,以及常见测试模式。
相关技能: godot-code-review 提供审查清单,dependency-injection 介绍适合测试的架构,export-pipeline 介绍 CI/CD 测试自动化。
框架选择
| 特性 | GUT | gdUnit4 |
|---|---|---|
| 语言 | GDScript 优先,有限支持 C# | GDScript + C#(一等支持) |
| 安装 | AssetLib 或 git 子模块 | AssetLib 或 git 子模块 |
| 编辑器集成 | 内置 GUT 面板 | 内置检查器 + 面板 |
| 模拟 | double() / stub() API | mock() / spy() API |
| 场景测试 | add_child_autofree() | auto_free() + 场景运行器 |
| CI 支持 | gut_cmdln.gd CLI 脚本 | runtest.sh / GdUnitCmdTool.gd |
| C# 支持 | 最少(仅限 GDScript 封装) | 原生 C# 断言 + 生命周期 |
| 成熟度 | 成熟(Godot 3 + 4) | 专注于 Godot 4,持续更新 |
| 最适用于 | 纯 GDScript 项目 | GDScript/C# 混合项目或纯 C# 项目 |
经验法则: 纯 GDScript 项目使用 GUT。C# 项目,或需要一等 C# 支持和场景运行器工具时,使用 gdUnit4。
TDD 工作流:RED-GREEN-REFACTOR
标准的测试驱动开发循环:编写一个失败的测试(RED),编写最少的代码使测试通过(GREEN),然后在不破坏测试的前提下重构。每个步骤都有各自的规范——不要跳过 RED(否则你会写出轻易就能通过的测试),也不要跳过 REFACTOR(技术债会不断累积)。
参见 references/tdd-workflow.md,其中通过 HealthComponent 的示例,演示 GDScript + C# 如何完成这三个步骤。
测试目录结构
res://
├── .gutconfig.json # GUT default config file (optional)
├── src/
│ └── components/
│ ├── health_component.gd
│ └── HealthComponent.cs
└── tests/
├── unit/
│ ├── test_health_component.gd # GUT: test_ prefix (default -gprefix)
│ └── HealthComponentTest.cs # gdUnit4 C#: [TestSuite] attribute
└── integration/
├── test_player_scene.gd
└── PlayerSceneTest.cs
命名约定
| 框架 | GDScript 文件 | C# 文件 | 测试方法前缀/属性 |
|---|---|---|---|
| GUT | test_*.gd | 不适用 | func test_*() |
| gdUnit4 | 任意名称(生成时为 *_test.gd) | *Test.cs | func test_*() / [TestCase] |
运行测试
两个框架都附带 CLI 运行器。GUT: 通过 godot --headless --path . -s addons/gut/gut_cmdln.gd -gexit 调用 addons/gut/gut_cmdln.gd(不加 -gexit,运行结束后进程不会退出)。gdUnit4: addons/gdUnit4/runtest.sh -a res://tests(封装了 bin/GdUnitCmdTool.gd;使用 --headless 时还需要 --ignoreHeadlessMode),或使用编辑器中的 GdUnit 停靠面板。CI:配置由标签或 PR 触发的 GitHub Action,安装 Godot、运行测试套件,并在失败时以非零状态码退出。
参见 references/running-tests.md,了解完整的 GUT 和 gdUnit4 CLI 调用方式,以及可直接复制粘贴的 GitHub Actions 工作流。
测试模式
四种常见模式:带节点的场景(在 before_each 中通过 add_child 实例化,并在 after_each 中释放)、信号测试(断言发射信号有效,并验证连接后发射信号会触发回调)、模拟/替身(在 GDScript 中使用 gdUnit4 的 mock();在 C# 中通过 @export 注入手写的伪实现)、异步(在测试中等待 yield、信号和帧)。
参见 references/testing-patterns.md,查看每种模式的完整代码(适用时包含 GDScript + C#)。
常见断言
GUT 断言
| 断言 | 描述 |
|---|---|
assert_eq(actual, expected) | 相等 |
assert_ne(actual, expected) | 不相等 |
assert_true(value) | 为真 |
assert_false(value) | 为假 |
assert_null(value) | 为 null |
assert_not_null(value) | 不为 null |
assert_gt(actual, expected) | 大于 |
assert_lt(actual, expected) | 小于 |
assert_gte(actual, expected) | 大于或等于 |
assert_lte(actual, expected) | 小于或等于 |
assert_has(collection, item) | 集合包含项目 |
assert_does_not_have(collection, item) | 集合不包含项目 |
assert_string_contains(str, sub) | 字符串包含子字符串 |
assert_almost_eq(actual, expected, margin) | 浮点数在误差范围内相等 |
assert_signal_emitted(obj, signal_name) | 已发出信号 |
assert_signal_not_emitted(obj, signal_name) | 未发出信号 |
gdUnit4 断言(GDScript + C#)
| GDScript | C# | 描述 |
|---|---|---|
assert_that(val).is_equal(exp) | AssertThat(val).IsEqual(exp) | 相等 |
assert_that(val).is_not_equal(exp) | AssertThat(val).IsNotEqual(exp) | 不相等 |
assert_that(val).is_true() | AssertThat(val).IsTrue() | 为真 |
assert_that(val).is_false() | AssertThat(val).IsFalse() | 为假 |
assert_that(val).is_null() | AssertThat(val).IsNull() | 为 null |
assert_that(val).is_not_null() | AssertThat(val).IsNotNull() | 不为 null |
assert_that(val).is_greater(exp) | AssertThat(val).IsGreater(exp) | 大于 |
assert_that(val).is_less(exp) | AssertThat(val).IsLess(exp) | 小于 |
assert_that(val).is_between(min, max) | AssertThat(val).IsBetween(min, max) | 在范围内(含边界) |
assert_that(arr).contains([a, b]) | AssertThat(arr).Contains(a, b) | 数组包含元素 |
assert_that(str).contains("sub") | AssertThat(str).Contains("sub") | 字符串包含子字符串 |
assert_that(val).is_equal_approx(exp, margin) | AssertThat(val).IsEqualApprox(exp, margin) | 浮点数在误差范围内 |
await assert_signal(mon).is_emitted("name") | await AssertSignal(node).IsEmitted("name") | 已发出信号 |
不要测试的内容
避免测试那些会增加噪声、却无法发现真实错误的内容:
- Godot 引擎内部机制 —— 不要断言
Node.add_child()能正常工作,或@export变量会显示在编辑器中 - 私有实现细节 —— 通过公共 API 测试行为;如果重构导致一个只覆盖私有状态的测试失败,那么这个测试就有问题
- 视觉/渲染输出 —— 像素级渲染结果很脆弱;应测试驱动视觉效果的数据
- 没有容差的时序敏感浮点数 —— 对物理值使用
assert_almost_eq/IsEqualApprox - 对内置功能进行简单封装的单行代码 —— 只返回字段的属性 getter 无需测试
- 所有可能的无效输入 —— 测试文档规定的契约,而非所有可以想象到的误用
检查清单
- 每个测试文件都符合所选框架的命名约定(GUT
test_*.gd;gdUnit4*_test.gd/*Test.cs) - 测试继承自正确的基类(
GutTest/GdUnitTestSuite);C# gdUnit4 测试套件是普通的[TestSuite]类 - C#:在 Godot 4.3 上,
gdUnit4.api固定为 4.3.x;在 5.x(Godot 4.4+)上,涉及节点或场景的测试套件需带有[RequireGodotRuntime] - 添加到场景树中的节点使用
add_child_autofree或auto_free,切勿手动调用queue_free() - 在触发信号的操作执行之前监听信号
- 对外部依赖使用 Mock/替身,而非被测单元
- 每个测试只覆盖一种行为(每个测试包含一个逻辑断言)
- CI 工作流在每次推送和 PR 时都以无头模式运行测试
- 对易不稳定的异步测试使用显式超时,而非任意设置休眠时长
- 合并前测试必须通过(只有在积极实现功能时才允许 RED)