Developer Tools 进阶 20m
在 UI 测试中处理中断和弹窗
Handle interruptions and alerts in UI tests
2020年6月24日
一句话判断
如果你的 UI 测试经常因为系统弹窗、权限请求或通知打断而失败,这场 Session 给出了系统级的解决方案——而且不需要写丑陋的 sleep 等待。
这场 Session 讲了什么
这场 Session 聚焦于 XCUITest 中一个被广泛忽视但极其重要的话题:如何处理测试执行过程中的各种中断。系统弹窗(如隐私权限请求、证书信任对话框)、通知横幅、来电、低电量提醒——这些不可预测的系统行为是 UI 测试不稳定性的主要来源之一。
演讲者首先介绍了 XCUITest 的 addUIInterruptionMonitor 机制,这是处理系统弹窗的标准方式。然后深入讨论了如何通过 XCUIApplication 的 launchArguments 和 launchEnvironment 预配置权限状态,让测试在已知环境下运行。Session 还覆盖了处理多应用交互测试时(比如从 App 跳转到 Safari 再返回)的中断管理策略。
后半部分,演讲者展示了一些高级技巧,包括如何封装可复用的中断处理器、如何在 Continuous Integration 环境中确保测试稳定性,以及如何调试那些”在我机器上能过”的 flaky test。
值得深挖的点
- addUIInterruptionMonitor 的生命周期陷阱:中断监视器注册后并非永久有效,它只在你调用
XCUIApplication.run()之后处于活跃状态。而且如果一个测试中有多个监视器,它们的匹配顺序是后进先出(LIFO)的。理解这个机制对于编写复杂测试流程至关重要,否则你会困惑于”为什么我的弹窗处理器没生效”。 - 权限预配置 vs 运行时处理:通过 launchArguments 重置权限状态(如
-ResetAuthorizationStatus YES)比依赖 UIInterruptionMonitor 更可靠。但并非所有系统弹窗都能通过这种方式避免,关键在于识别哪些中断可以预防、哪些必须运行时处理。
代码片段
// 注册系统中断监视器处理隐私权限弹窗
let monitor = addUIInterruptionMonitor(
withDescription: "处理系统权限弹窗"
) { alert in
if alert.buttons["Allow"].exists {
alert.buttons["Allow"].tap()
return true // 已处理
}
return false // 未处理,交给下一个监视器
}
// 触发需要权限的操作
app.buttons["Request Location"].tap()
// 必须交互一下才能激活监视器
app.tap()
removeUIInterruptionMonitor(monitor)
// 通过 launchArguments 预配置权限,避免弹窗出现
let app = XCUIApplication()
app.launchArguments = [
"-ResetAuthorizationStatus", "YES",
"-UITesting", "YES"
]
app.launchEnvironment = [
"ENABLE_MOCK_LOCATION": "YES"
]
app.launch()
// 现在测试可以在无弹窗环境下运行
// 封装可复用的弹窗处理器
func handleSystemAlerts(
in app: XCUIApplication,
timeout: TimeInterval = 5
) {
let monitor = addUIInterruptionMonitor(
withDescription: "通用弹窗处理器"
) { alert in
let allowButtons = ["Allow", "OK", "Allow While Using App"]
for buttonTitle in allowButtons {
if alert.buttons[buttonTitle].exists {
alert.buttons[buttonTitle].tap()
return true
}
}
return false
}
// 触发交互以激活监视器
app.tap()
removeUIInterruptionMonitor(monitor)
}
最佳实践
- 优先通过 launchArguments 消除可预测的系统弹窗,而非在运行时逐个处理
- addUIInterruptionMonitor 注册后必须有一次 UI 交互才能激活,这是很多人踩的坑
- 在 CI 环境中运行测试时,确保测试设备的通知和来电已关闭
- 将弹窗处理逻辑封装成独立的 helper 方法,保持测试代码的可读性
- 避免在测试中使用
sleep()等待弹窗出现,使用XCTWaiter或expectation进行条件等待
还什么值得关注
- Xcode 12 对 UI 测试的启动速度有优化,测试 bundle 加载更快了
- 如果你在做多 App 交互测试,注意 XCUIApplication 的 activate/deactivate 方法在不同 iOS 版本上的行为差异
- 搭配 “Get your test results faster” 一起看,可以建立完整的测试稳定性保障体系
WWDC 2020