You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XCTest UI测试无法点击iOS推送通知Banner问题求助

问题根因&解决方案

点击崩溃/无响应是三个核心原因导致的:

  1. 发送推送后没有显式等待通知元素加载为可点击状态,元素刚出现在视图层级时isHittable属性为false,直接触发tap要么无响应要么报元素不存在
  2. 元素匹配路径冗余,iOS15上SpringBoard的通知横幅本身就是BannerNotification类型,不需要嵌套查otherElements
  3. 部分测试逻辑里提前调用了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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 01:15:35