XCTest UI测试无法点击iOS推送通知Banner问题求助
问题根因&解决方案
点击崩溃/无响应是三个核心原因导致的:
- 发送推送后没有显式等待通知元素加载为可点击状态,元素刚出现在视图层级时
isHittable属性为false,直接触发tap要么无响应要么报元素不存在 - 元素匹配路径冗余,iOS15上SpringBoard的通知横幅本身就是
BannerNotification类型,不需要嵌套查otherElements - 部分测试逻辑里提前调用了
app.launch()把应用拉到前台,横幅会被系统立即收起到通知中心,自然无法点击
方案1:直接点击顶部弹出的通知横幅(无需修改通知为持久化)
这个方案不需要改应用通知配置,完全自动化,适配iOS15.x版本:
func testWhenPushNotificationOpenThenCorrectPageIsShown() { // 先确保应用完全杀进程,不要提前launch let app = XCUIApplication() app.terminate() let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") springboard.activate() // 发送推送 sendPushNotification() // 等待通知横幅出现,最长等5秒 let pushBanner = springboard.banners["MyApp, now, My notification text"] let isBannerAppear = pushBanner.waitForExistence(timeout: 5) XCTAssertTrue(isBannerAppear, "推送通知横幅未弹出") // 核心:等元素可点击再触发tap,iOS15上刚出现的banner有几百毫秒不可交互 let hittablePredicate = NSPredicate(format: "isHittable == true") let bannerHittableExpectation = expectation(for: hittablePredicate, evaluatedWith: pushBanner, handler: nil) wait(for: [bannerHittableExpectation], timeout: 2) // 直接点击,不需要嵌套查子元素 pushBanner.tap() // 等应用启动,校验目标页面 XCTAssertTrue(app.wait(for: .runningForeground, timeout: 10)) XCTAssertTrue(app.otherElements["目标页面的唯一标识"].waitForExistence(timeout: 5)) }
如果还是出现点击无响应,可以把普通tap替换为坐标点击,避开元素交互阻塞的问题:
// 替换上面代码里的pushBanner.tap() let tapCoordinate = pushBanner.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)) tapCoordinate.tap()
方案2:下拉通知中心点击通知(适配横幅自动消失的场景)
如果推送横幅自动消失太快,用下拉通知中心的方案,注意不要提前启动app:
func testTapNotificationFromNotificationCenter() { let app = XCUIApplication() app.terminate() let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") springboard.activate() sendPushNotification() // 等2秒让通知收到并自动收起 sleep(2) // 下拉打开通知中心:从顶部状态栏(y<50的位置)往下拖到屏幕中间 let startPoint = springboard.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.02)) let endPoint = springboard.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)) startPoint.press(forDuration: 0.1, thenDragTo: endPoint) // 等通知中心加载,找到对应通知cell let notifCell = springboard.cells.containing(.staticText, identifier: "My notification text").firstMatch XCTAssertTrue(notifCell.waitForExistence(timeout: 3), "通知中心未找到对应推送") // iOS15点击通知cell会弹出"打开"按钮,等按钮出现点击 notifCell.tap() let openButton = springboard.buttons["打开"] XCTAssertTrue(openButton.waitForExistence(timeout: 2)) openButton.tap() // 校验页面 XCTAssertTrue(app.wait(for: .runningForeground, timeout: 5)) XCTAssertTrue(app.otherElements["目标页面的唯一标识"].waitForExistence(timeout: 10)) }
额外注意点
- 不要在发送推送前后调用
app.launch(),杀进程状态下直接启动app会让系统取消横幅弹出,直接把通知存到通知中心 - 测冷启动场景时,测试开始时调用
app.terminate()确保应用不在后台运行即可,不需要手动杀进程 - 不需要修改通知为持久化模式,上述两个方案在默认的临时通知模式下可正常运行
- 如果测试设备开了通知摘要、专注模式,先手动关闭,避免通知被系统拦截不弹出
内容的提问来源于stack exchange,提问作者Wonton
相关产品推荐
相关产品推荐

