MangoVM 工程指南

云端 Mac 的 XCTestPlan 配置漂移审计实战

云端 Mac 的 XCTestPlan 配置漂移审计实战

同一提交在开发者电脑上跑完全部测试,进入云端 Mac 后却只执行了一部分,最常见的原因并不是测试代码,而是 .xctestplan 已经悄悄漂移:有人在 Xcode 里临时跳过用例、关闭诊断项,或把只适合本机的环境变量保存进计划文件。解决办法是把 XCTestPlan 当作构建输入,而不是图形界面的附属设置。

先固定测试契约的边界

一个可复现的测试入口至少包含共享 Scheme、XCTestPlan、执行目标和命令行参数。先确认 Scheme 与计划确实能被命令行发现:

WORKSPACE="${WORKSPACE:-App.xcworkspace}"
SCHEME="${SCHEME:-App}"

xcodebuild \
  -workspace "$WORKSPACE" \
  -scheme "$SCHEME" \
  -showTestPlans

如果输出中没有预期计划,先检查 Scheme 是否设为 Shared、计划文件是否加入仓库,以及 Scheme 的 Test 动作是否引用了它。不要用 CI 参数临时补一个不存在的计划,这会让本地入口与流水线继续分叉。

计划中应明确评审以下内容:

项目 需要确认的事实 常见漂移
Test Targets 哪些单元测试和 UI 测试参与执行 新目标未加入
Selected Tests 是否只运行指定用例 调试选择被提交
Skipped Tests 每个跳过项是否有负责人和期限 失败用例被永久隐藏
Configurations 语言、地区和启动参数 本机配置覆盖默认配置
Diagnostics 崩溃、线程和性能诊断策略 为提速而意外关闭
Parallelization 哪些目标允许并行 共享状态测试互相干扰

跳过测试不是修复。任何新增的 skipped test 都应当像代码变更一样进入评审,并说明恢复条件。

生成可读的规范化差异

XCTestPlan 是结构化文件,直接审查原始文本容易被字段顺序和自动生成标识干扰。可以在仓库中保存一份规范化基线,只移除不影响执行语义的配置标识:

PLAN="App.xctestplan"
CURRENT=".ci/xctestplan.current.json"
BASELINE=".ci/xctestplan.baseline.json"

mkdir -p .ci

plutil -convert json -o - "$PLAN" |
  jq -S 'del(.configurations[]?.id)' > "$CURRENT"

diff -u "$BASELINE" "$CURRENT"

首次接入时,人工核对 $CURRENT 后将它复制为基线并提交。后续流水线只生成当前文件并执行 diff。目标、跳过项、环境变量、参数和诊断选项都必须保留;不要为了得到“干净差异”继续删除业务字段。

规范化脚本也属于测试基础设施。脚本改动与计划改动应放在同一个合并请求中展示,否则一次过滤规则放宽就可能让后续漂移全部失去可见性。

拦截敏感值与主机依赖

计划文件适合保存变量名,不适合保存令牌、密码、私钥内容或开发者目录。先递归提取启用的环境变量:

plutil -convert json -o - App.xctestplan |
  jq -r '
    .. |
    objects |
    .environmentVariableEntries? // empty |
    .[]? |
    select(.enabled == true) |
    [.key, .value] |
    @tsv
  '

检查输出时重点拦截三类内容:看起来像密钥的长字符串、/Users/某人/ 形式的绝对路径,以及只在交互式 Shell 中存在的工具路径。真实敏感值应由 CI 的受控环境在运行时注入,测试代码只读取变量名,并在缺失时给出明确错误。

避免配置覆盖优先级失控

XCTestPlan、Scheme、xcodebuild 参数和测试代码都能设置启动参数。建议规定单一优先级:计划保存稳定默认值,CI 只注入敏感值与本次运行标识,测试代码不修改进程级配置。若流水线使用 -only-testing-skip-testing,应把参数写入可审查脚本,不能藏在任务面板的临时输入框中。

用固定目标执行并保存证据

先通过 xcrun simctl list devices available 选择当前节点已安装的设备,并把 UDID 作为流水线输入。相比模糊指定“最新系统”,固定设备与运行时更容易解释差异。

DEVICE_UDID="${DEVICE_UDID:?Set DEVICE_UDID from simctl}"
RESULT_PATH="${RESULT_PATH:-artifacts/CI.xcresult}"

rm -rf "$RESULT_PATH"
mkdir -p "$(dirname "$RESULT_PATH")"

xcodebuild test \
  -workspace "$WORKSPACE" \
  -scheme "$SCHEME" \
  -testPlan CI \
  -destination "platform=iOS Simulator,id=$DEVICE_UDID" \
  -resultBundlePath "$RESULT_PATH"

无论成功还是失败,都应保存 .xcresult、完整命令、提交标识、Xcode 版本和所选设备 UDID。不要只截取最后几十行日志;测试未执行、进程崩溃和断言失败需要不同证据,结果包能保留测试层级、附件与诊断信息。

并行执行要从保守值开始。依赖同一数据库、固定端口或共享文件的测试目标不应直接开启并行。先把状态隔离做完整,再逐个允许并行,而不是用重试掩盖竞争条件。

把审计变成合并门禁

最终门禁应按固定顺序运行:验证 Scheme 能发现计划、生成规范化文件、比较基线、扫描敏感值与绝对路径、核对跳过列表,最后才执行测试。这样配置错误会在启动模拟器之前失败,节省排队和诊断时间。

提交前可用以下清单复核:

当 XCTestPlan 的每次变化都能在代码评审中被读懂,“本机全绿、云端漏测”就不再是偶发谜题,而会变成一条能在执行前被阻止的配置差异。

常见问题

为什么不能只依赖 Xcode Scheme 里的测试设置?

Scheme 只能说明入口,实际测试目标、配置、环境变量、语言地区和跳过列表可能保存在 XCTestPlan 中。CI 应同时固定共享 Scheme、计划文件和执行命令。

XCTestPlan 中可以保存访问令牌吗?

不应保存真实令牌。计划文件只保留变量名或无敏感性的默认值,真实值应由受控的 CI 环境在运行时注入,并避免写入测试附件和日志。

规范化 XCTestPlan 会不会漏掉重要变更?

只应移除随机生成且不影响执行语义的标识,并保留测试目标、选中与跳过项、配置名称、参数和诊断选项。规范化规则本身也必须进入代码审查。

独享物理节点

为开发流水线配置固定的云端 Mac

MangoVM 提供 M4 与 M4 Pro 两档 Apple Silicon 物理节点。选择租期、区域和存储附加项后,即可核对完整订单明细。

选择配置并订购