mcpField
F
fix-flaky-tests

fix-flaky-tests

github.com/tuist测试与质量35505 次安装31 周前更新
代码仓库

关于 fix-flaky-tests

Fix Flaky Tests 是一项工作流技能,用于调查间歇性测试失败、针对性修复代码,并通过重复运行进行验证,适合处理 Tuist 测试数据和 Xcode 测试运行的开发者。

主要功能

  1. 01查找被标记为不稳定的测试
  2. 02检查可靠性指标和运行历史
  3. 03查看失败信息及源代码位置
  4. 04识别常见的时序和状态问题
  5. 05通过重复运行验证修复

适用场景

  1. 01排查项目中的不稳定测试
  2. 02将失败追溯到测试源代码
  3. 03在重复或并行运行中确认修复效果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tuist/agent-skills --skill fix-flaky-tests

安装后在 Claude Code 中输入/fix-flaky-tests确认已加载。

详细介绍

修复不稳定测试

快速开始

通常你会收到一个 Tuist 测试用例 URL 或标识符。按以下步骤调查并修复:

  1. 运行 tuist test case show <id-or-identifier> --json,获取测试的可靠性指标。
  2. 运行 tuist test case run list Module/Suite/TestCase --flaky --json,查看不稳定运行的模式。
  3. 对失败的不稳定运行执行 tuist test case run show <test-case-run-id> --json,获取失败消息和文件路径。
  4. 阅读报告路径和行号处的测试源代码,找出不稳定模式并修复。
  5. 多次运行测试进行验证,确认测试能够稳定通过。

如果没有提供具体测试,请从下面的「发现」部分开始。

发现

未提供具体测试用例时,查找项目中的所有不稳定测试:

tuist test case list --flaky --json --page-size 50

这会返回当前标记为不稳定的所有测试用例。重要字段:

  • module.name / suite.name / name — 测试标识符
  • avg_duration — 有助于确定优先级(先修复运行较快的单元测试)
  • is_quarantined — 测试是否已隔离

分类策略:

  1. 按套件对测试分组——同一套件中有多个不稳定测试时,它们通常有共同根因。
  2. 检查失败是否共享同一个 test_run_id——在同一次运行中全部失败的测试,可能是进程崩溃导致被终止,而非各自存在测试缺陷。
  3. 查看失败消息进行分类:测试逻辑缺陷还是基础设施问题(网络错误、服务器 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

代码分析

  1. 打开 failures[0].path 指向的文件,并跳转到 failures[0].line_number。
  2. 阅读完整的测试函数及其设置和清理逻辑。
  3. 确定以下常见不稳定模式中,哪一种适用于此测试。
  4. 检查该测试是否与同一套件中的其他测试共享状态。

常见不稳定模式

计时和异步问题
  • 等待缺失:异步操作完成前,测试就检查结果。修复方法:使用 await、带超时的预期条件或轮询。
  • 竞态条件:多个并发操作访问共享状态。修复方法:同步访问,或使用串行队列。
  • 超时值硬编码:sleep(1) 或固定延迟在 CI 上过短。修复方法:使用基于条件的等待,而非固定延迟。
共享状态
  • 测试污染:一个测试修改了另一个测试所依赖的全局或静态状态。修复方法:在 setUp/tearDown 中重置状态,或为每个测试使用唯一实例。
  • 单例污染:共享单例在测试之间保留状态。修复方法:注入依赖项,或重置单例。
  • 文件系统残留:测试留下的文件影响后续运行。修复方法:使用临时目录并进行清理。
环境依赖
  • 网络调用:测试会访问可能缓慢或不可用的真实服务。解决方法:模拟网络调用。
  • 日期/时间敏感性:测试依赖当前时间或时区。解决方法:注入时钟或冻结时间。
  • 文件系统路径:硬编码路径在不同环境中可能有所不同。解决方法:使用相对路径或临时目录。
顺序依赖
  • 隐式顺序:只有在另一个设置了所需状态的测试之后运行时,测试才能通过。解决方法:让每个测试都能独立运行。
  • 并行执行冲突:测试单独运行时正常,但并发运行时失败。解决方法:为每个测试使用唯一资源。
崩溃(通过 crash_report 识别)
  • EXC_BREAKPOINT / SIGTRAP — 对 nil 值强制解包,Swift 前置条件失败
  • EXC_BAD_ACCESS / SIGSEGV — 使用已释放的对象或悬空指针
  • EXC_CRASH / SIGABRT — 未捕获的 Objective-C 异常

修复实现

识别出问题模式后:

  1. 应用能解决根本原因的最小修复。
  2. 不要重构无关代码。
  3. 如果修复需要测试工具(如模拟对象或辅助函数),先检查是否已有可用工具。

验证

重复运行测试

使用 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 级别的并行度下出现,因此很难在本地复现。按以下顺序尝试这些策略:

  1. 提高并行度:添加 -parallel-testing-enabled YES,并发运行测试套件。
  2. 运行更大范围的测试套件:不要只运行单个测试,而是运行整个模块(例如 -only-testing ModuleTests),以增加对共享资源的争用。
  3. 线程 Sanitizer:启用 TSan 运行,以确定性地检测数据竞争。注意:TSan 会增加开销,可能改变时序,因此某些竞态在启用 TSan 时可能不会触发。
xcodebuild test -workspace <workspace> -scheme <scheme> -only-testing <module> -enableThreadSanitizer YES
  1. 大范围、多次迭代:结合以上所有方法,以并行方式运行整个模块并进行多次迭代。

如果竞态条件无法在本地复现,但能从代码中证明确实存在非线程安全问题(例如未同步地修改共享状态),修复仍然有效。使用上述相同的复现策略确认测试通过,以验证修复。在提交说明中注明,该修复通过代码分析和失败日志解决了仅在 CI 中出现的竞态条件。

完成检查清单

  • 已识别偶发问题的根本原因
  • 已应用有针对性的修复
  • 已验证测试多次运行均能稳定通过
  • 未引入新的测试依赖或共享状态
  • 已使用描述清晰的消息提交修复

评论

支持 Markdown