Highlight
XCTest 的
XCTSkipAPI(Xcode 11.4 引入)让测试在运行时条件不满足时返回显式 skip 结果,Xcode 和 CI 报告会记录跳过位置、原因和设备维度结果。
核心内容
集成测试经常依赖运行环境。一个测试可能只适合 iPad,因为功能只在 iPad 上启用;另一个测试可能调用 iOS 13.4 才有的 API;还有测试依赖一台偶尔维护的服务器。这些条件在编译期很难判断,只能等测试运行时才知道。
过去处理这种情况很尴尬。直接 return 会让测试显示为通过,报告会暗示代码已经被验证;强行失败又会把环境限制当成产品问题,浪费排查时间。XCTSkip 给 XCTest 增加了第三种结果:通过、失败、跳过。
session 的例子是一个叫 Play Garden 的小 App。它新增了 pointer interaction 测试,但 pointer interaction 只在 iOS 13.4 之后可用,App 也只在 iPad 上启用这项交互。测试方法开头先检查系统版本和设备类型;条件不满足时,测试抛出 skip,而不是伪装成通过或失败。
这个结果会出现在 Xcode 的多个入口。源码旁会显示跳过注释,Test Navigator 可以筛选 skipped tests,test report 会列出文件、行号和 reason。CI 里同一个测试跑在三台设备上时,报告可以同时展示一个 iPad 通过、其他设备跳过,这比一个含糊的绿色结果更接近真实测试覆盖。
详细内容
1. 把运行时前置条件变成测试结果
(00:14)session 一开始就定义了问题:有些测试的依赖无法轻易 mock,例如设备类型、系统版本和外部服务状态。XCTSkip 让测试作者在运行时声明“这次没有执行”,并把这个状态写入测试结果。
(01:20)这个 API 在 Xcode 11.4 引入。测试现在可以通过、失败,也可以被标记为 explicit skip。Xcode 用专门的 skip 图标区分这些测试,让报告说明测试套件实际验证了什么。
func testExample() throws {
/// Example usage: skip test if device is not an iPad
try XCTSkipUnless(UIDevice.current.userInterfaceIdiom == .pad,
"Pointer interaction tests are for iPad only")
// test...
}
关键点:
testExample()标记为throws,因为 skip 会通过抛出 XCTest 可识别的结果中断当前测试。XCTSkipUnless在表达式为false时跳过测试。UIDevice.current.userInterfaceIdiom == .pad对应 transcript 里的 iPad-only pointer interaction 场景。- reason 字符串会进入 Xcode 和 CI 报告,团队成员能看到跳过原因。
2. 用 guard 处理系统版本要求
(02:19)Play Garden 的 pointer interaction 测试还有一个系统版本条件:pointer interaction 在 iOS 13.4 引入。旧系统上运行这条测试没有意义,因为待测 API 不存在。
(05:52)session 展示了直接抛出 XCTSkip struct 的写法。它适合和 guard 搭配,用一条前置条件挡住后续测试逻辑。
func testExample() throws {
/// Example usage: skip test if OS version is older than iOS 13.4
guard #available(iOS 13.4, *) else {
throw XCTSkip("Pointer interaction tests can only run on iOS 13.4+")
}
// test...
}
关键点:
#available(iOS 13.4, *)把系统版本要求写在测试入口。throw XCTSkip(...)直接返回 skip 结果,测试报告不会把旧系统环境误报成失败。- reason 说明了跳过条件和最低系统版本。
- 后续测试代码只在满足前置条件的环境中运行,断言失败才更可能代表真实产品问题。
3. 理解 XCTSkipIf 与 XCTSkipUnless
(05:25)API 层面有两个 throwing function:XCTSkipIf 和 XCTSkipUnless。它们参数相同,差别在触发条件。
try XCTSkipUnless(UIDevice.current.userInterfaceIdiom == .pad,
"Pointer interaction tests are for iPad only")
关键点:
XCTSkipIf在表达式为true时跳过。XCTSkipUnless在表达式为false时跳过。- 两者都适合写在测试方法开头,先声明运行条件,再进入真正的断言。
- 选择哪个函数取决于条件表达式读起来是否自然。
4. 在 Xcode 和 CI 里查看 skip
(03:22)当测试在旧设备上运行时,Xcode 把绿色 pass 图标换成灰色 skip 图标,并在源码中标出跳过位置和原因。
(03:37)Test Navigator 会显示 skipped 状态,还能用底部按钮筛选只看跳过的测试。test report 展开后会显示文件、行号和 reason。Xcode 12 新增的 assistant 按钮可以打开 secondary editor,把报告和源代码并排展示。
(04:34)CI 报告会保留设备维度。示例里同一个测试跑在三台设备上,一个 iPad 通过,其他设备跳过;展开每个设备结果后,可以看到对应的跳过位置和 reason。
try XCTSkipUnless(UIDevice.current.userInterfaceIdiom == .pad,
"Pointer interaction tests are for iPad only")
关键点:
- 同一条测试可以在不同 destination 上得到不同结果。
- skip reason 是 CI 排查材料的一部分,不需要回到本地猜测为什么没跑。
- 通过、失败、跳过分开统计,测试覆盖情况更接近实际执行情况。
- 这类信息适合放进 pull request 或 nightly test 的 result bundle 分析流程。
核心启发
- 做什么:按设备能力跳过 UI 测试。 为什么值得做:session 的 iPad pointer interaction 示例说明,设备类型是运行时条件。怎么开始:在测试方法开头用
XCTSkipUnless(UIDevice.current.userInterfaceIdiom == .pad, "...")保护只适合 iPad 的交互测试。 - 做什么:把最低系统版本写进测试入口。 为什么值得做:旧系统缺少待测 API 时,失败结果会误导排查。怎么开始:用
guard #available(...) else { throw XCTSkip(...) }包住依赖新 API 的测试。 - 做什么:在 CI 报告里追踪 skipped tests。 为什么值得做:同一条测试在不同设备上可能出现 pass 和 skip,session 展示了设备维度结果。怎么开始:保留 result bundle,按 skipped filter 查看跳过数量、位置和 reason。
- 做什么:为集成测试建立前置条件检查。 为什么值得做:外部服务器维护、硬件能力缺失或账号状态异常都属于运行时条件。怎么开始:把检查放在测试第一段,条件不满足时抛出
XCTSkip,条件满足后再执行断言。 - 做什么:把 skip reason 写成可排查文本。 为什么值得做:Xcode 源码注释、Test Navigator、test report 和 CI 展开项都会显示 reason。怎么开始:用包含条件和行动线索的字符串,例如最低系统版本、目标设备或服务状态。
关联 Session
- Write tests to fail — 讲如何让 XCTest 和 UI 测试在失败时留下可诊断的 result bundle 上下文。
- Handle interruptions and alerts in UI tests — 处理 UI 测试里的系统弹窗和预期 alert,和运行时测试条件管理相邻。
- Get your test results faster — 介绍 test plans、timeouts、execution time allowances 和并行化,适合作为 CI 测试反馈的下一步。
- Triage test failures with XCTIssue — 用 XCTIssue 记录结构化失败,与 skip 一起完善测试结果语义。
- Eliminate animation hitches with XCTest — 用 XCTest 度量动画和滚动 hitch,展示 XCTest 在质量回归检测中的另一类实践。
评论
GitHub Issues · utterances