一个普通的按钮改名、图标替换或约束调整,都可能让 iOS 页面失去可读标签、缩小点击区域,或者在大字号下截断文字。人工巡检很难覆盖每次提交。更稳妥的做法,是在云端 Mac 上固定模拟器与应用状态,用 XCTest 执行无障碍审计,再把失败变成可复现的合并门禁。
先定义门禁边界
不要一开始遍历整个产品。先选择登录后首页、核心编辑页和提交确认页等高频路径,每条用例只验证一个稳定状态。动画、随机推荐、当前时间和网络返回值都会改变元素树,应在测试模式下关闭或注入固定数据。
第一轮建议检查四类问题:
| 检查项 | 常见缺陷 | 门禁处理 |
|---|---|---|
| 元素描述 | 图标按钮没有可读名称 | 直接失败 |
| 点击区域 | 控件可见但难以点按 | 直接失败 |
| 对比度 | 前景与背景难以区分 | 核对设计后修复 |
| 文字布局 | 大字号下截断或重叠 | 直接失败 |
自动审计负责发现机器能够稳定判断的问题,不负责证明整个交互已经易用。阅读顺序、提示是否清楚以及复杂手势,仍要保留人工检查。
给测试一个确定的画面
UI 测试最怕“同一个入口,不同的页面”。应用应识别仅供测试使用的启动参数,在启动时清空临时状态、载入固定数据,并关闭非必要动画。参数只改变测试环境,不能进入正式业务逻辑。
let app = XCUIApplication()
app.launchArguments = [
"-ui-testing",
"-reset-demo-state",
"-disable-animations"
]
app.launchEnvironment["TEST_LOCALE"] = "zh-Hans"
app.launch()
测试数据应在应用进程内准备,而不是让 UI 测试依赖实时接口。需要覆盖加载、空数据、错误和正常结果时,为每种状态建立独立参数。这样失败后只需复制命令,不必等待某个远端条件再次出现。
固定元素定位方式
不要用按钮标题或坐标定位关键控件。标题会随本地化变化,坐标会随窗口和字号变化。为可交互元素设置语义稳定的 accessibilityIdentifier:
checkoutButton.accessibilityIdentifier = "checkout.submit"
cartSummary.accessibilityIdentifier = "checkout.summary"
标识符描述职责而非视觉位置。使用 footer.orangeButton 之类名称,会在改版后失去意义;checkout.submit 则可以跨布局继续使用。
执行针对性的 XCTest 审计
系统版本支持相关 API 时,可以对已进入稳定状态的页面执行审计。先等待关键元素出现,再开始检查,避免把加载过程中的临时布局当成缺陷。
func testCheckoutAccessibility() throws {
let app = XCUIApplication()
app.launchArguments = ["-ui-testing", "-reset-demo-state"]
app.launch()
let submit = app.buttons["checkout.submit"]
XCTAssertTrue(submit.waitForExistence(timeout: 10))
if #available(iOS 17.0, *) {
try app.performAccessibilityAudit(for: [
.sufficientElementDescription,
.hitRegion,
.contrast,
.textClipped
])
}
}
不要随手建立全局忽略列表。确需暂时排除时,应限定到明确的页面、元素标识和问题类型,并在代码审查中写明删除条件。否则忽略项会逐渐变成永久盲区。
在云端 Mac 上固定执行条件
先查看当前可用的模拟器设备与运行时,再由流水线传入确定的 destination。不要假设每台节点都已经存在同名设备。
xcrun simctl list devices available
xcrun simctl list runtimes
export SIM_DESTINATION='platform=iOS Simulator,name=iPhone 15,OS=17.5'
set -o pipefail
xcodebuild test \
-project ExampleApp.xcodeproj \
-scheme ExampleAppUITests \
-destination "$SIM_DESTINATION" \
-only-testing:ExampleAppUITests/AccessibilityTests \
-resultBundlePath "$PWD/TestResults/Accessibility.xcresult"
实际运行时应把设备名与系统版本作为流水线变量,并与节点已安装的运行时保持一致。执行前创建全新的模拟器,或在复用设备时先关闭应用并清理测试状态。并行任务不要共享同一个模拟器,否则语言、权限弹窗和前一条用例的残留状态会互相污染。
云端 Mac 的机型和节点选择不改变测试设计原则。需要新增执行环境时,在控制台确认当前可选配置,并记录 Xcode 版本、运行时版本、语言和 destination,保证失败能够在同一组条件下重跑。
分层运行并处理常见误报
每次合并请求只运行核心页面、默认语言和一个标准字号,把反馈时间控制在团队可接受范围内。主分支再扩展到多语言、横竖屏、深色外观和动态字体。不要把所有组合塞进一个测试方法;按页面与状态拆分后,失败名称本身就能说明范围。
遇到不稳定失败时,按以下顺序检查:
- 确认关键元素是否已完成加载,而不是仅存在于元素树。
- 比对语言、区域、字号、方向和外观设置。
- 检查测试数据是否经过上一次运行修改。
- 单独重跑失败方法,确认问题能否稳定出现。
- 保存测试结果包、执行命令与环境字段,再判断是产品缺陷还是测试隔离不足。
门禁规则也应分级。缺少描述、无法点击和文字重叠适合阻止合并;尚未完成设计确认的对比度问题可以先记录,但必须设定负责人和处理期限。最终目标不是获得一份永远全绿的报告,而是让每次失败都能定位到页面、状态、元素和环境条件。
常见问题
无障碍审计可以完全替代人工检查吗?
不能。自动审计适合发现缺少描述、点击区域过小、对比度不足和文字截断等结构化问题,但阅读顺序、操作语义与真实交互体验仍需人工验证。
为什么无障碍测试在本地通过,在云端 Mac 上却失败?
先核对模拟器运行时、语言、区域、动态字体、界面方向和测试数据是否一致。任何一项漂移都可能改变布局或元素树,应把这些条件固定在测试启动参数中。
流水线应在何时执行完整无障碍审计?
核心页面的快速审计适合每次合并请求执行;覆盖多语言、动态字体和多种页面状态的完整矩阵可在主分支或定时任务中运行。
把下一次构建放到云端 Mac 上运行。
比较三档 Apple Silicon 配置,在五个海外节点中选择适合当前工作流的区域。