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()
我正在使用 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()