关于 fix-flaky-tests
Fix Flaky Tests 是一项工作流技能,用于调查间歇性测试失败、针对性修复代码,并通过重复运行进行验证,适合处理 Tuist 测试数据和 Xcode 测试运行的开发者。
主要功能
- 01查找被标记为不稳定的测试
- 02检查可靠性指标和运行历史
- 03查看失败信息及源代码位置
- 04识别常见的时序和状态问题
- 05通过重复运行验证修复
适用场景
- 01排查项目中的不稳定测试
- 02将失败追溯到测试源代码
- 03在重复或并行运行中确认修复效果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ tuist/ agent-skills --skill fix-flaky-tests安装后在 Claude Code 中输入/fix-flaky-tests确认已加载。
详细介绍
修复不稳定测试
快速开始
通常你会收到一个 Tuist 测试用例 URL 或标识符。按以下步骤调查并修复:
- 运行
tuist test case show <id-or-identifier> --json,获取测试的可靠性指标。 - 运行
tuist test case run list Module/Suite/TestCase --flaky --json,查看不稳定运行的模式。 - 对失败的不稳定运行执行
tuist test case run show <test-case-run-id> --json,获取失败消息和文件路径。 - 阅读报告路径和行号处的测试源代码,找出不稳定模式并修复。
- 多次运行测试进行验证,确认测试能够稳定通过。
如果没有提供具体测试,请从下面的「发现」部分开始。
发现
未提供具体测试用例时,查找项目中的所有不稳定测试:
tuist test case list --flaky --json --page-size 50
这会返回当前标记为不稳定的所有测试用例。重要字段:
module.name/suite.name/name— 测试标识符avg_duration— 有助于确定优先级(先修复运行较快的单元测试)is_quarantined— 测试是否已隔离
分类策略:
- 按套件对测试分组——同一套件中有多个不稳定测试时,它们通常有共同根因。
- 检查失败是否共享同一个
test_run_id——在同一次运行中全部失败的测试,可能是进程崩溃导致被终止,而非各自存在测试缺陷。 - 查看失败消息进行分类:测试逻辑缺陷还是基础设施问题(网络错误、服务器 502 错误、重试时发生冲突)。
调查
1. 获取测试用例指标
你可以传入 UUID 或 Module/Suite/TestCase 标识符:
tuist test case show <id> --json
tuist test case show Module/Suite/TestCase --json
重要字段:
reliability_rate— 成功运行的百分比(越高越好)flakiness_rate— 最近 30 天内被标记为不稳定的运行百分比total_runs/failed_runs— 运行量信息last_status— 当前状态
2. 查看不稳定运行历史
tuist test case run list Module/Suite/TestCase --flaky --json
标识符格式为 ModuleName/SuiteName/TestCaseName;没有套件时则为 ModuleName/TestCaseName。这只会返回被检测为不稳定的运行。
3. 查看完整运行历史
tuist test case run list Module/Suite/TestCase --json --page-size 20
请留意以下模式:
- 是否只在特定分支上失败?
- 是否只在 CI 上失败(
is_ci: true),还是本地也会失败? - 失败是否集中在特定提交附近?
4. 获取失败详情
tuist test case run show <test-case-run-id> --json
重要字段:
failures[].message— 断言或错误消息failures[].path— 源文件路径failures[].line_number— 失败的准确行号failures[].issue_type— 问题类型(assertion_failure 等)repetitions— 如果存在,会显示重试行为(通过/失败序列)test_run_id— 此次执行所属的测试运行crash_report— 崩溃报告数据(测试运行器崩溃时存在);包含exception_type、signal、exception_subtype和triggered_thread_frames
代码分析
- 打开
failures[0].path指向的文件,并跳转到failures[0].line_number。 - 阅读完整的测试函数及其设置和清理逻辑。
- 确定以下常见不稳定模式中,哪一种适用于此测试。
- 检查该测试是否与同一套件中的其他测试共享状态。
常见不稳定模式
计时和异步问题
- 等待缺失:异步操作完成前,测试就检查结果。修复方法:使用
await、带超时的预期条件或轮询。 - 竞态条件:多个并发操作访问共享状态。修复方法:同步访问,或使用串行队列。
- 超时值硬编码:
sleep(1)或固定延迟在 CI 上过短。修复方法:使用基于条件的等待,而非固定延迟。
共享状态
- 测试污染:一个测试修改了另一个测试所依赖的全局或静态状态。修复方法:在 setUp/tearDown 中重置状态,或为每个测试使用唯一实例。
- 单例污染:共享单例在测试之间保留状态。修复方法:注入依赖项,或重置单例。
- 文件系统残留:测试留下的文件影响后续运行。修复方法:使用临时目录并进行清理。
环境依赖
- 网络调用:测试会访问可能缓慢或不可用的真实服务。解决方法:模拟网络调用。
- 日期/时间敏感性:测试依赖当前时间或时区。解决方法:注入时钟或冻结时间。
- 文件系统路径:硬编码路径在不同环境中可能有所不同。解决方法:使用相对路径或临时目录。
顺序依赖
- 隐式顺序:只有在另一个设置了所需状态的测试之后运行时,测试才能通过。解决方法:让每个测试都能独立运行。
- 并行执行冲突:测试单独运行时正常,但并发运行时失败。解决方法:为每个测试使用唯一资源。
崩溃(通过 crash_report 识别)
EXC_BREAKPOINT/SIGTRAP— 对 nil 值强制解包,Swift 前置条件失败EXC_BAD_ACCESS/SIGSEGV— 使用已释放的对象或悬空指针EXC_CRASH/SIGABRT— 未捕获的 Objective-C 异常
修复实现
识别出问题模式后:
- 应用能解决根本原因的最小修复。
- 不要重构无关代码。
- 如果修复需要测试工具(如模拟对象或辅助函数),先检查是否已有可用工具。
验证
重复运行测试
使用 xcodebuild 内置的重复运行支持,重复运行指定测试,直到失败:
xcodebuild test -workspace <workspace> -scheme <scheme> -only-testing <module>/<suite>/<test> -test-iterations <count> -run-tests-until-failure
这会最多运行测试 <count> 次,并在首次失败时停止。根据测试耗时选择迭代次数:快速单元测试使用 50–100 次,较慢的集成测试或验收测试使用 2–5 次。
修复前复现问题
应用修复前,先尝试在本地复现偶发失败。成功复现可以确认根因分析,并让你直接验证修复。使用上文“重复运行测试”中的方法;如果怀疑存在并发问题,则使用下文的竞态条件策略。
某些偶发问题场景,尤其是竞态条件、CI 特有的时序问题或依赖环境的失败,可能难以或无法在本地复现。如果经过合理尝试仍无法复现,可根据代码分析和失败日志着手修复。即使无法在本地复现,只要有明确证据表明存在 bug(例如共享状态未同步、TOCTOU 模式),修复就是有效的。
复现竞态条件
竞态条件和并发 bug 通常只会在 CI 级别的并行度下出现,因此很难在本地复现。按以下顺序尝试这些策略:
- 提高并行度:添加
-parallel-testing-enabled YES,并发运行测试套件。 - 运行更大范围的测试套件:不要只运行单个测试,而是运行整个模块(例如
-only-testing ModuleTests),以增加对共享资源的争用。 - 线程 Sanitizer:启用 TSan 运行,以确定性地检测数据竞争。注意:TSan 会增加开销,可能改变时序,因此某些竞态在启用 TSan 时可能不会触发。
xcodebuild test -workspace <workspace> -scheme <scheme> -only-testing <module> -enableThreadSanitizer YES
- 大范围、多次迭代:结合以上所有方法,以并行方式运行整个模块并进行多次迭代。
如果竞态条件无法在本地复现,但能从代码中证明确实存在非线程安全问题(例如未同步地修改共享状态),修复仍然有效。使用上述相同的复现策略确认测试通过,以验证修复。在提交说明中注明,该修复通过代码分析和失败日志解决了仅在 CI 中出现的竞态条件。
完成检查清单
- 已识别偶发问题的根本原因
- 已应用有针对性的修复
- 已验证测试多次运行均能稳定通过
- 未引入新的测试依赖或共享状态
- 已使用描述清晰的消息提交修复