Xcode7 | Xcode UI 测试 |如何处理位置服务警报?

Xcode7 | Xcode UI Tests | How to handle location service alert?

我正在使用 UIApplication、XCUIElement 和 XCUIElementQuery 中介绍的 XCUIElementQuery 为我的其中一个应用编写 UI 测试用例38=]9.

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

问题似乎是因为警报是由 OS 本身提出的,所以它不存在于应用程序的元素 sub-tree.

我记录了以下内容:

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 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 Interuption Handler"。这是我们告诉框架如何处理警报出现的方式。

其次,在显示警报后,您必须与界面进行交互。只需点击该应用程序即可正常工作,但这是必需的。

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

"Location Dialog" 只是一个字符串,用于帮助开发人员识别访问了哪个处理程序,它并不特定于警报类型。

Xcode 7.0

以下将取消 Xcode 7 Beta 6 中的单个 "system alert":

let app = XCUIApplication()
app.launch()
// trigger location permission dialog

app.alerts.element.collectionViews.buttons["Allow"].tap()

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

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

要点按允许位置提醒,您可以致电 element.tap() 其中元素是屏幕上的任何元素。 因此,在调用 tap 之后,辅助功能将在警告时点击允许,而不是点击你的元素

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

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

它检查警报是否显示,如果显示它会点击它

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

也可能与我已经使用 addUIInterruptionMonitor 用于不同的警报有关。我尝试重新排序它们,但没有任何区别。当你有两个时,可能是 9 中的问题,或者可能是我用错了它们。无论如何,下面的代码都有效。 :)

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

在 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

我在 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()
    }

这适用于所有语言:

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

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

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