【问题标题】:Xcode7 | Xcode UI Tests | How to handle location service alert?Xcode7 | Xcode UI 测试 |如何处理位置服务警报?
【发布时间】:2015-10-23 03:15:55
【问题描述】:

我正在使用 Xcode7/iOS 9 中引入的 XCUIApplication、XCUIElement 和 XCUIElementQuery 为我的一个应用程序编写 UI 测试用例。

我遇到了障碍。测试用例中的屏幕之一需要 iOS 的定位服务。正如预期的那样,提示用户允许使用定位服务,并带有标题为:Allow “App name” to access your location while you use the app?AllowDon't Allow 按钮的警报。

问题似乎是因为警报是由操作系统本身呈现的,所以它不存在于应用程序的元素子树中。

我记录了以下内容:

print("XYZ:\(app.alerts.count)")//0
var existence = app.staticTexts["Allow “App Name” to access your location while you use the app?"].exists
print("XYZ:\(existence)")//false
existence  = app.buttons["Allow"].exists
print("XYZ:\(existence)") //false

即使是 UI 录制也生成了类似的代码:

XCUIApplication().alerts["Allow “App Name” to access your location while you use the app?"].collectionViews.buttons["Allow"].tap()

我还没有找到任何可以让我解决这个问题的 API。例如:

  • 点击屏幕上的某个位置
  • 在应用外获取警报

那么我该如何克服呢?有没有办法配置测试目标,这样就不需要位置服务授权。

【问题讨论】:

标签: xcode xctest ios9 xcode7-beta4 xcode-ui-testing


【解决方案1】:

在 xcode 9.1 上,只有在测试设备装有 iOS 11 时才会处理警报。不适用于较旧的 iOS 版本,例如 10.3 等。参考:https://forums.developer.apple.com/thread/86989

要处理警报,请使用:

//Use this before the alerts appear. I am doing it before app.launch()

let allowButtonPredicate = NSPredicate(format: "label == 'Always Allow' || label == 'Allow'")
//1st alert
_ = addUIInterruptionMonitor(withDescription: "Allow to access your location?") { (alert) -> Bool in
    let alwaysAllowButton = alert.buttons.matching(allowButtonPredicate).element.firstMatch
    if alwaysAllowButton.exists {
        alwaysAllowButton.tap()
        return true
    }
    return false
}
// One interruption monitor is sufficient for multiple alerts

【讨论】:

    【解决方案2】:

    这适用于所有语言:

    let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
    let allowBtn = springboard.buttons.element(boundBy: 1)
    if allowBtn.exists {
        allowBtn.tap()
    }
    

    【讨论】:

    • 对我来说,如果我使用索引 1,它就可以工作。对我来说,任何人都有 3 个按钮似乎很奇怪
    • 位置权限警报是具有三个按钮的系统警报示例(截至 iOS 15)
    【解决方案3】:

    这是我通过点击(双按钮)对话框中的第二个按钮来接受任何语言的通知权限警报的操作。允许按钮在右侧,因此索引为 1。

    let handler = addUIInterruptionMonitor(withDescription: "System Dialog") { (alert) -> Bool in
        alert.buttons.element(boundBy: 1).tap()
        return true
    }
    app.tap()
    

    【讨论】:

      【解决方案4】:

      我让它在 Xcode 9.4.1 上使用它,诀窍是等待弹出窗口出现。

      // wait for location service popup to appear
          let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
          let allowBtn = springboard.buttons["Allow"]
          expectation(for: NSPredicate(format: "exists == true"), evaluatedWith: allowBtn, handler: nil)
          waitForExpectations(timeout: 10, handler: nil)
      
          //allow location service
          if allowBtn.exists {
            allowBtn.tap()
          }
      

      【讨论】:

        【解决方案5】:

        Xcode 9

            let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
            let allowBtn = springboard.buttons["Allow"]
            if allowBtn.exists {
                allowBtn.tap()
            }
        

        Xcode 8.3.3

            _ = addUIInterruptionMonitor(withDescription: "Location Dialog") { (alert) -> Bool in
                alert.buttons["Allow"].tap()
                return true
            }
            app.buttons["Request Location"].tap()
            app.tap() // need to interact with the app for the handler to fire
        

        请注意,它有点不同,因为方法名称现在是 addUIInterruptionMonitor 并以 withDescription 作为参数

        Xcode 7.1

        Xcode 7.1 终于修复了系统警报问题。但是,有两个小问题。

        首先,您需要在显示警报之前设置“UI 中断处理程序”。这是我们告诉框架如何处理出现的警报的方式。

        其次,在呈现警报后,您必须与界面进行交互。只需点按应用即可,但这是必需的。

        addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in
            alert.buttons["Allow"].tap()
            return true
        }
        
        app.buttons["Request Location"].tap()
        app.tap() // need to interact with the app for the handler to fire
        

        “位置对话框”只是一个字符串,用于帮助开发人员识别访问了哪个处理程序,它并不特定于警报类型。

        Xcode 7.0

        以下内容将关闭 Xcode 7 Beta 6 中的单个“系统警报”:

        let app = XCUIApplication()
        app.launch()
        // trigger location permission dialog
        
        app.alerts.element.collectionViews.buttons["Allow"].tap()
        

        Beta 6 引入了一系列针对 UI 测试的修复,我相信这就是其中之一。

        另外请注意,我直接在-alerts 上调用-element。在XCUIElementQuery 上调用-element 会强制框架选择屏幕上的“唯一”匹配元素。这对于您一次只能看到一个的警报非常有用。但是,如果您尝试对一个标签执行此操作并且有两个标签,则框架将引发异常。

        【讨论】:

        • 它似乎在 Xcode 7.1 中崩溃了。我提交了一个错误报告,rdar://22498241。我建议任何遇到这种情况的人复制它。
        • 我面临推送通知警报,当我键入“po XCUIApplicaction()”时它没有列出任何想法?我使用 Xcode 7.1
        • @blackjacx 我已经更新了 Xcode 7.1 的答案,它不再使测试套件崩溃。
        • 是的,我就是这样做的,我可以确认这是可行的!
        • 关于 8.2 的任何更新?我注意到我现在甚至不必解除警报。它会自动点击允许。还有人注意到这一点吗?
        【解决方案6】:

        这是唯一对我有用的东西。使用 Xcode 9 fwiw。

        也可能与我已经在使用 addUIInterruptionMonitor 进行不同的警报有关。我尝试重新排序它们并没有什么不同。当您有两个时,可能是 9 中的问题,或者可能是我使用错误。无论如何,下面的代码都有效。 :)

        let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
        let allowBtn = springboard.buttons["Allow"]
        if allowBtn.exists {
            allowBtn.tap()
        }
        

        【讨论】:

        • 太棒了,这对我来说就像一个魅力。旧的解决方案在 Xcode 9 上不再适用。这是我在寻找答案的几个小时中找到的最好的解决方案。这个解决方案应该是最重要的。 :) 谢谢乔。
        • 这非常有效 - 这应该被标记为正确答案,因为 apparently 正确答案没有!
        • 这在 XCode 9.1 中对我来说就像一个冠军,但在 XCode 9.3 中它似乎被打破了。
        【解决方案7】:

        如果要检查警报是否显示,只需检查按钮是否存在:

        if (app.alerts.element.collectionViews.buttons["Dismiss"].exists)
        {
        app.alerts.element.collectionViews.buttons["Dismiss"].tap()
        }
        

        它检查警报是否正在显示,如果正在显示,它将点击它

        【讨论】:

          【解决方案8】:

          要点击允许位置警报,您可以致电 element.tap() 其中 element 是屏幕上的任何元素。 因此,在调用点击后,可访问性将在警报时点击允许,然后点击您的元素

          【讨论】:

            猜你喜欢
            • 2016-02-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-07-03
            • 1970-01-01
            • 2016-08-13
            • 1970-01-01
            相关资源
            最近更新 更多