32

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

我遇到了路障。测试用例中的屏幕之一需要 iOS 的定位服务。正如预期的那样,系统会提示用户允许使用定位服务,并带有标题为:Allow “App name” to access your location while you use the app?带有Allow&Don'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。例如:

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

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

4

8 回答 8

37

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 测试引入了一系列修复,我相信这就是其中之一。

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

于 2015-08-26T13:25:10.657 回答
8

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

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

let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let allowBtn = springboard.buttons["Allow"]
if allowBtn.exists {
    allowBtn.tap()
}
于 2017-11-02T21:53:56.550 回答
3

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

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

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

于 2015-11-02T16:38:17.627 回答
3

在 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
于 2017-12-20T13:30:25.780 回答
3

我让它在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()
    }
于 2018-08-07T06:08:54.397 回答
1

这适用于所有语言:

let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let allowBtn = springboard.buttons.element(boundBy: 1)
if allowBtn.exists {
    allowBtn.tap()
}
于 2018-08-18T08:49:45.743 回答
0

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

于 2015-10-27T18:48:27.303 回答
0

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

let handler = addUIInterruptionMonitor(withDescription: "System Dialog") { (alert) -> Bool in
    alert.buttons.element(boundBy: 1).tap()
    return true
}
app.tap()
于 2019-06-22T23:59:59.113 回答