PAYATHON 2026

为什么 xcodebuild 每次测试的覆盖率和 target 列表都不同?

数据小陈

仅凭现有现象,无法确定是 Xcode 的缺陷、测试执行差异,还是覆盖率转换流程出了问题。先比较每次 .xcresult 的原始覆盖率报告,确认变化发生在哪一层。

Coverage 标签页里的 target 列表也在变化,应优先排查。如果参与汇总的代码范围不同,即使部分文件的执行情况相同,总覆盖率也可能改变。重新运行 CI 只能说明问题没有稳定复现,不能证明结果已经正确。

先保存每次运行的独立结果

让 runTestForXcresults.sh 为每次测试生成独立的 result bundle、构建日志和测试日志,避免结果被后续步骤覆盖,或误读其他运行留下的文件。

同时记录以下信息:

  • Xcode 版本、实际使用的 Developer Directory,以及 SDK 版本。
  • 完整的 xcodebuild 命令,包括 scheme、test plan、configuration 和 destination。
  • 代码提交、依赖解析结果,以及测试筛选和并行执行设置。
  • 测试是否成功结束,有没有失败、跳过、崩溃或重试。

将这些信息与报告一起保留,以便确认两份覆盖率报告的执行条件是否一致。

直接比较 xcresult 中的覆盖率报告

在提供 xccov 的 Xcode 环境中,可以导出报告进行比较。例如,将下面的路径替换为实际生成的 result bundle:

xcrun xccov view --report --json Run-01.xcresult > Run-01-coverage.json
xcrun xccov view --report --json Run-02.xcresult > Run-02-coverage.json

如果当前 Xcode 不接受该命令形式,先运行 xcrun xccov help,以本机工具支持的参数为准。

比较时不要只看总百分比,应依次检查:

  1. target 集合是否相同,是否有 target 缺失或增加。
  2. 同名 target 对应的路径、文件集合和可执行行数是否相同。
  3. 同一文件的可执行行数与已覆盖行数是否变化。

若使用文本 diff,先按 target 和文件路径排序,避免把 JSON 数组的顺序变化误认为内容变化。覆盖率通常按已覆盖的可执行行数与可执行行总数计算,因此分子和分母都要比较。

根据差异出现的位置继续排查

如果原始报告的 target 或文件集合已经不同,先检查构建和测试过程:两次选择的测试和构建的目标是否相同,测试是否完整结束。报告中没有某个 target,不能直接认为它的覆盖率是 0%;还需要检查构建日志和覆盖率采集情况。

如果 target、文件集合和可执行行数一致,但已覆盖行数不同,就检查实际执行的测试及其行为。测试失败、提前退出、重试,以及依赖共享状态、执行顺序或外部服务的测试,都可以作为待验证的方向。不能仅凭覆盖率波动认定其中某项就是原因。

如果 xccov 导出的内容一致,而转换后导入 Sonar 的数据不同,应重点检查转换脚本和导入过程,包括输入路径、文件匹配、重复报告合并,以及是否混入历史报告。

用单变量实验缩小范围

可以继续使用现有复现脚本,但每组实验只调整一个条件:

  • 暂时关闭并行测试,观察差异是否仍然出现。
  • 仅运行一个 test target,再逐步加入其他测试。
  • 为每次运行指定独立的 DerivedData 路径,与复用构建产物的运行方式对比。
  • 检查命令行是否覆盖了 test plan 中的覆盖率设置。

关闭并行测试或隔离 DerivedData 都只是诊断手段。即使结果变得稳定,也要继续确定触发条件,不能直接宣布问题已修复。

将覆盖率设为 “all targets” 只调整了采集范围的配置,无法单独证明每次实际构建、执行和生成报告的过程都相同。这项设置未能解决问题,也不足以确定具体原因。

保持 Sonar 的统计口径一致

80% 门槛需要对应明确的代码范围:哪些业务 target、文件和 packages 应纳入统计,应在各次运行之间保持一致。不要通过忽略缺失 target 或只保留覆盖率较高的报告来消除波动。

下一步最有价值的材料是同一提交下两次结果不同的 .xcresult、对应的 xccov JSON、完整命令和日志。先找出具体发生变化的 target 或文件,再决定是否进一步检查配置、测试行为、转换脚本,或提交工具缺陷报告。

备注:内容仅供参考。