关于 test-flakiness
面向 Claude 的 Skill,通过分析多次运行的测试结果日志来识别和分类不稳定测试,并建议隔离、修复或监控。运行次数少于 3 次时,会将发现标为疑似。
主要功能
- 01分析 CI 与本地测试日志
- 02分类可能的不稳定原因
- 03按失败频率评估发现
- 04建议隔离、修复或监控
- 05经批准更新隔离清单
适用场景
- 01调查反复出现的 CI 失败
- 02评估回归审查标记的测试
- 03跟踪多次运行中的疑似不稳定项
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ donchitos/ claude-code-game-studios --skill test-flakiness安装后在 Claude Code 中输入/test-flakiness确认已加载。
详细介绍
!bash "${CLAUDE_SKILL_DIR}/../../hooks/yaml-helper.sh" resolve_config --keys automation
自动化模式:解析 modes.automation(project.local.yaml →
project.yaml → 默认值 collaborative)。每次调用 AskUserQuestion 和
每次文件写入都遵循 .claude/docs/automation-modes.md
(collaborative 始终询问 · guided 仅重大事项询问 · autonomous 记录后继续;
automation_always_ask 类别始终提示)。
测试不稳定性检测
不稳定测试是指在代码没有任何更改的情况下,有时通过、有时失败的测试。不稳定测试在某些方面甚至比没有测试更糟——它们会让团队习惯于忽略 CI 红色运行结果,从而掩盖真实故障。此技能可识别不稳定测试,解释可能的原因,并建议是隔离还是修复每个测试。
输出: 更新 tests/regression-suite.md 的隔离部分 + 可选的
production/qa/flakiness-report-[date].md
运行时机:
- 润色阶段(测试已运行多次;统计信号可靠)
- 开发者开始把 CI 故障当作“可能是不稳定”而不予理会时
/regression-suite识别出需要诊断的隔离测试后
1. 解析参数
模式:
/test-flakiness [ci-log-path]— 分析指定的 CI 运行日志文件/test-flakiness scan— 扫描.github/或标准日志输出目录中的所有可用 CI 日志/test-flakiness registry— 读取现有regression-suite.md隔离部分,并为已知的不稳定测试提供修复指导- 不带参数 — 自动检测:如果可以访问 CI 日志,则运行
scan,否则运行registry
2. 定位 CI 日志数据
选项 A — GitHub Actions(首选)
检查测试结果制品:
ls -t .github/ 2>/dev/null
ls -t test-results/ 2>/dev/null
对于 Godot 项目:GdUnit4 会在 reports/ 下输出兼容 JUnit 格式的 XML 结果(其默认报告文件夹为 res://reports/)。检查 reports/,以及你保存运行结果的任何 test-results/ 目录,查找 .xml 文件。
对于 Unity 项目:game-ci 测试运行器默认会将 NUnit XML 输出到 test-results/。
对于 Unreal 项目:自动化日志位于 Saved/Logs/。搜索
Result={Success} 和 Result={Fail}——每项测试都会输出
Test Completed. Result={<status>}
(docs/engine-reference/unreal/current-best-practices.md,“命令行”)。
选项 B — 本地日志文件
如果提供了路径参数,直接读取该文件。
选项 C — 没有可用日志数据
如果未找到日志:
“未找到 CI 日志数据。要检测不稳定测试,此技能需要多次运行的测试结果历史记录。可选方案:
- 至少运行测试套件 3 次并收集输出日志
- 检查 CI 流水线输出,并将日志保存到
test-results/- 运行
/test-flakiness registry,检查tests/regression-suite.md中已标记为不稳定的测试”
停止并询问用户要采用哪个方案。
3. 解析测试结果
对于找到的每个 CI 日志或结果文件,解析:
JUnit XML 格式(GdUnit4):
- 搜索
<testcase name=获取测试名称 - 搜索
<failure或<error以识别失败 - 解析
classname和name属性,获取完整测试标识符
NUnit XML 格式(Unity — /smoke-check
读取的 <test-run> 元素所在文件):
- 每项测试都是一个
<test-case元素;其fullname属性是标识符 - 其
result属性为Passed、Failed、Inconclusive或Skipped(docs/engine-reference/unity/current-best-practices.md,“命令行”); 只有Passed和Failed会纳入历史记录
纯文本日志:
- 搜索通过/失败模式:
- Godot:测试名称附近的
PASSED/FAILED - Unreal:
Result={Success}/Result={Fail} - Unity:
Test passed/Test failed
- Godot:测试名称附近的
建立表格:test_id → [run1_result, run2_result, run3_result, ...]
4. 识别不稳定测试
如果某个测试在没有代码变更的情况下,多次运行的结果历史中既出现 PASS,也出现 FAIL,则该测试为不稳定测试。
不稳定程度阈值:
- 高度不稳定:超过 25% 的运行失败——立即隔离
- 中度不稳定:5–25% 的运行失败——尽快调查并修复
- 低度/疑似不稳定:1–5% 的运行失败——持续监控;也可能确实是罕见故障
运行次数少于 3 次时,无论失败率如何,所有发现都属于疑似不稳定。 两次运行中失败一次,看起来失败率是 50%,但这只是一个数据点:不要隔离该测试,将其标记为疑似不稳定,并询问是否有更多运行数据。 上述等级和隔离措施适用于运行次数达到 3 次及以上的情况。
对于每个不稳定测试,判定可能的原因:
原因分类
| 原因 | 症状 | 修复方向 |
|---|---|---|
| 时序/异步 | 等待信号或计时器后失败;通过率与系统负载相关 | 添加显式的等待/同步;避免基于时间的延迟 |
| 顺序依赖 | 在特定其他测试之后运行时失败;单独运行时通过 | 添加适当的设置/清理;确保测试相互隔离 |
| 随机种子 | 间歇性失败且无明显规律;涉及 RNG | 传入显式种子;不要在测试中使用 randf() |
| 资源泄漏 | 在一次测试运行中越靠后越容易失败 | 在 teardown 中修复清理逻辑;检查孤立节点(Godot)或对象释放(Unity) |
| 外部状态 | 先前测试遗留的文件、场景或全局状态存在时失败 | 隔离测试与文件系统;使用内存 mock |
| 浮点数 | 在 == 0.5 之类的比较中失败 | 使用 epsilon 比较(is_equal_approx、Assert.AreApproximately) |
| 场景/预制体加载竞态 | 场景尚未就绪时失败 | 实例化后等待一帧;使用 await get_tree().process_frame |
使用 Grep 检查测试文件中的时序调用、randf、全局状态访问或浮点数相等比较,以缩小可能原因的范围。
5. 建议采取的措施
对于每个不稳定测试:
隔离(高度不稳定):
“立即隔离此测试。使用引擎自身的机制跳过该测试,在
tests/regression-suite.md的隔离部分记录该测试,并在移除隔离前修复根本原因。”
按引擎选择跳过机制——只列出项目所用引擎对应的机制:
- Godot(gdUnit4)、GDScript:在测试函数上使用跳过参数 —
func test_x(_do_skip := true, _skip_reason := "flaky: [cause]")。gdUnit4 会在addons/gdUnit4/src/core/GdUnitTestSuiteScanner.gd中读取参数名do_skip和skip_reason(允许带前导_);此行为适用于 gdUnit4 6.2.2; 请在已安装版本的对应文件中确认。 - Godot(gdUnit4)、C#:无法从源码核实 — gdUnit4 的 C# 测试特性不在
addons/gdUnit4/源码中,且docs/engine-reference/godot/未涵盖这些特性。请在隔离部分记录,并询问用户如何跳过其 C# 测试;不要臆造特性。 - Unity(NUnit):
[Ignore("flaky: [cause]")]— NUnit 3 要求提供原因 - Unreal:无法从源码核实 —
docs/engine-reference/unreal/未说明如何跳过自动化测试。请在隔离部分记录,并询问用户如何在其 CI 中排除某个测试;不要臆造标记。
尽快调查并修复(中度):
“此测试间歇性地不可靠。可能的根本原因是 [cause]。 建议的修复:[根据原因分类提出具体修复方案]。暂时不要隔离——直接修复测试。”
监控(低度/疑似不稳定):
“此测试显示出疑似不稳定。隔离前请收集更多运行数据。在回归套件中将其标记为‘疑似’。”
6. 生成报告
对话摘要
## Flakiness Detection Results
**Runs analysed**: [N]
**Tests tracked**: [N]
### Flaky Tests Found
| Test | System | Fail Rate | Confidence | Likely Cause | Recommendation |
|------|--------|-----------|------------|--------------|----------------|
| [test_name] | [system] | [N]% | confirmed | Timing | Quarantine + fix async |
| [test_name] | [system] | [N]% | confirmed | Float comparison | Fix: use epsilon compare |
| [test_name] | [system] | [N]% | suspected (fewer than 3 runs) | Order dependency | Collect more runs before acting |
### Clean Tests (no flakiness detected)
[N] tests ran across [N] runs with consistent results — no flakiness detected.
### Data Limitations
[Note if fewer than 5 runs were available — fewer runs = less statistical confidence]
7. 更新回归套件 + 可选报告文件
询问:“我可以更新 tests/regression-suite.md 的隔离部分,
添加发现的不稳定测试吗?”
如果同意:使用 Edit 将条目追加到 Quarantined Tests 表格中。
绝不要移除现有的隔离条目——只能添加新条目。
另行询问:“我可以将完整的不稳定性报告写入
production/qa/flakiness-report-[date].md 吗?”
完整报告包含逐个测试的分析、原因详情和 针对引擎的修复代码片段。
写入后:
- 对于每个隔离的测试:“添加针对引擎的跳过注解, 在 CI 中禁用此测试。根本原因修复后重新启用。”
- 对于符合修复条件的测试:“[test] 的修复很简单——
将第 [N] 行的相等比较改为使用
is_equal_approx。” - 总结:“应用所有隔离注解后,CI 应该会通过。 请在发布门禁前安排修复这 [N] 个隔离测试。”
协作协议
- 绝不删除测试文件——隔离意味着添加注解 + 列出测试,而不是移除
- 统计置信度很重要——运行次数少于 3 次时,将发现标记为 “疑似”而非“已确认”;询问是否有更多运行数据
- 修复始终是目标——隔离只是临时措施;即使建议隔离,也要指出修复方向
- 写入前先询问——更新回归套件和报告文件都需要明确批准。写入时:结论:已完成——不稳定性报告已写入。拒绝时:结论:受阻——用户拒绝写入。
- CI 中的不稳定性是团队问题——清楚列出相关测试和建议采取的措施;不要在团队不知情的情况下悄悄隔离测试