XCUITesting for permission popup: alert appears, but UIInterruptionMonitor does not fire
XCUITesting for permission popup: alert appears, but UIInterruptionMonitor does not fire
我想写一个这样的测试:
当我的应用进入某个窗格时,它应该请求使用相机的权限。
我想测试窗格是否出现。我正在使用 XC 的内置 UITest 框架来执行此操作。根据我在 google 和此处找到的内容,我似乎应该执行以下操作:
let dialogAppearedExpectation = expectationWithDescription("Camera Permission Dialog Appears")
addUIInterruptionMonitorWithDescription("Camera Permission Alert") { (alert) -> Bool in
dialogAppearedExpectation.fulfill()
return true
}
goToCameraPage()
waitForExpectationsWithTimeout(10) { (error: NSError?) -> Void in
print("Error: \(error?.localizedDescription)")
}
测试从失败开始,太棒了。我实现了 goToCameraPage,它正确地导致 "give permission" 弹出窗口出现。但是,我希望这会触发中断监视器。但是,没有捕获到此类中断,并且不会发生实现。
我在某处读到您应该在对话框出现后 app.tap()
执行此操作。但是,当我这样做时,它会单击 "allow" 按钮。对话框消失,仍然没有处理中断。
有没有什么方法可以不考虑 "alerts" 或无法处理权限对话框?我什至进去用一个只看 app.alerts
的东西替换了中断位,但结果是空的,即使我正在看模拟器中的弹出窗口。
谢谢!我正在使用 Xcode7.2, iOS 9.2 模拟器 iPhone 6s.
我也注意到了这个问题。似乎中断处理程序是 运行 异步的,无法断言它们是否被调用。同样等待期望似乎完全可以防止中断监视器 运行ning。看起来系统正在等待期望的实现,而期望正在等待中断监视器触发。典型的死锁案例。
但是,我发现了一个使用基于 NSPredicate
的期望的相当古怪的解决方案:
var didShowDialog = false
expectation(for: NSPredicate() {(_,_) in
XCUIApplication().tap() // this is the magic tap that makes it work
return didShowDialog
}, evaluatedWith: NSNull(), handler: nil)
addUIInterruptionMonitor(withDescription: "Camera Permission Alert") { (alert) -> Bool in
alert.buttons.element(boundBy: 0).tap() // not sure if allow = 0 or 1
didShowDialog = true
return true
}
goToCameraPage()
waitForExpectations(timeout: 10) { (error: Error?) -> Void in
print("Error: \(error?.localizedDescription)")
}
显然,在谓词块中执行 XCUIApplication().tap()
以某种方式允许中断监视器 运行,即使测试用例正在等待期望。
我希望这对你和我一样有效!
pancake 的答案有效,但前提是应用程序是第一次测试。如果应用之前已经在同一个模拟器上进行过测试,则权限已经授予应用,因此永远不会出现警告,测试将失败。
我的方法是等待应该出现在应用程序中的元素,而不是等待处理完警报对话框。如果警告对话框在应用程序上方,应用程序的元素将不会 "exist" 因为它不是 reachable/tappable.
let alertHandler = addUIInterruptionMonitor(withDescription: "Photos or Camera Permission Alert") { (alert) -> Bool in
if alert.buttons.matching(identifier: "OK").count > 0 {
alert.buttons["OK"].tap()
// Required to return focus to app
app.tap()
return true
} else {
return false
}
}
app.buttons["Change Avatar"].tap()
if !app.buttons["Use Camera"].waitForExistence(timeout: 5.0) {
// Cause the alert handler to be invoked if the alert is currently shown.
XCUIApplication().swipeUp()
}
_ = app.buttons["Use Camera"].waitForExistence(timeout: 2.0)
removeUIInterruptionMonitor(alertHandler)
所以 对我有用。但是,我认为它可以简化。出现系统警报时,确实出现了某种奇怪的死锁或竞争条件。
而不是 NSPredicate
我只是在应该显示系统警报之后和尝试 XCUIApplication().tap()
之前使用了 sleep(2)
。
我还决定使用 XCUIApplication().swipeUp()
,因为它不太可能干扰测试。
使用 Facebook 登录的示例
class LoginWithFacebookTest: XCTestCase {
let app = XCUIApplication()
var interruptionMonitor: NSObjectProtocol!
let alertDescription = "“APP_NAME” Wants to Use “facebook.com” to Sign In"
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
self.removeUIInterruptionMonitor(interruptionMonitor)
}
func loginWithFacebookTest() {
app.launch()
self.interruptionMonitor = addUIInterruptionMonitor(withDescription: self.alertDescription) { (alert) -> Bool in
// check for a specific button
if alert.buttons["Continue"].exists {
alert.buttons["Continue"].tap()
return true
}
return false
}
let loginWithFacebook = app.otherElements["login with facebook"]
loginWithFacebook.tap()
// Sleep to give the alert time to show up
sleep(2)
// Interact with the app to get the above monitor to fire
app.swipeUp()
}
}
我想写一个这样的测试:
当我的应用进入某个窗格时,它应该请求使用相机的权限。
我想测试窗格是否出现。我正在使用 XC 的内置 UITest 框架来执行此操作。根据我在 google 和此处找到的内容,我似乎应该执行以下操作:
let dialogAppearedExpectation = expectationWithDescription("Camera Permission Dialog Appears")
addUIInterruptionMonitorWithDescription("Camera Permission Alert") { (alert) -> Bool in
dialogAppearedExpectation.fulfill()
return true
}
goToCameraPage()
waitForExpectationsWithTimeout(10) { (error: NSError?) -> Void in
print("Error: \(error?.localizedDescription)")
}
测试从失败开始,太棒了。我实现了 goToCameraPage,它正确地导致 "give permission" 弹出窗口出现。但是,我希望这会触发中断监视器。但是,没有捕获到此类中断,并且不会发生实现。
我在某处读到您应该在对话框出现后 app.tap()
执行此操作。但是,当我这样做时,它会单击 "allow" 按钮。对话框消失,仍然没有处理中断。
有没有什么方法可以不考虑 "alerts" 或无法处理权限对话框?我什至进去用一个只看 app.alerts
的东西替换了中断位,但结果是空的,即使我正在看模拟器中的弹出窗口。
谢谢!我正在使用 Xcode7.2, iOS 9.2 模拟器 iPhone 6s.
我也注意到了这个问题。似乎中断处理程序是 运行 异步的,无法断言它们是否被调用。同样等待期望似乎完全可以防止中断监视器 运行ning。看起来系统正在等待期望的实现,而期望正在等待中断监视器触发。典型的死锁案例。
但是,我发现了一个使用基于 NSPredicate
的期望的相当古怪的解决方案:
var didShowDialog = false
expectation(for: NSPredicate() {(_,_) in
XCUIApplication().tap() // this is the magic tap that makes it work
return didShowDialog
}, evaluatedWith: NSNull(), handler: nil)
addUIInterruptionMonitor(withDescription: "Camera Permission Alert") { (alert) -> Bool in
alert.buttons.element(boundBy: 0).tap() // not sure if allow = 0 or 1
didShowDialog = true
return true
}
goToCameraPage()
waitForExpectations(timeout: 10) { (error: Error?) -> Void in
print("Error: \(error?.localizedDescription)")
}
显然,在谓词块中执行 XCUIApplication().tap()
以某种方式允许中断监视器 运行,即使测试用例正在等待期望。
我希望这对你和我一样有效!
pancake 的答案有效,但前提是应用程序是第一次测试。如果应用之前已经在同一个模拟器上进行过测试,则权限已经授予应用,因此永远不会出现警告,测试将失败。
我的方法是等待应该出现在应用程序中的元素,而不是等待处理完警报对话框。如果警告对话框在应用程序上方,应用程序的元素将不会 "exist" 因为它不是 reachable/tappable.
let alertHandler = addUIInterruptionMonitor(withDescription: "Photos or Camera Permission Alert") { (alert) -> Bool in
if alert.buttons.matching(identifier: "OK").count > 0 {
alert.buttons["OK"].tap()
// Required to return focus to app
app.tap()
return true
} else {
return false
}
}
app.buttons["Change Avatar"].tap()
if !app.buttons["Use Camera"].waitForExistence(timeout: 5.0) {
// Cause the alert handler to be invoked if the alert is currently shown.
XCUIApplication().swipeUp()
}
_ = app.buttons["Use Camera"].waitForExistence(timeout: 2.0)
removeUIInterruptionMonitor(alertHandler)
所以
而不是 NSPredicate
我只是在应该显示系统警报之后和尝试 XCUIApplication().tap()
之前使用了 sleep(2)
。
我还决定使用 XCUIApplication().swipeUp()
,因为它不太可能干扰测试。
使用 Facebook 登录的示例
class LoginWithFacebookTest: XCTestCase {
let app = XCUIApplication()
var interruptionMonitor: NSObjectProtocol!
let alertDescription = "“APP_NAME” Wants to Use “facebook.com” to Sign In"
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
self.removeUIInterruptionMonitor(interruptionMonitor)
}
func loginWithFacebookTest() {
app.launch()
self.interruptionMonitor = addUIInterruptionMonitor(withDescription: self.alertDescription) { (alert) -> Bool in
// check for a specific button
if alert.buttons["Continue"].exists {
alert.buttons["Continue"].tap()
return true
}
return false
}
let loginWithFacebook = app.otherElements["login with facebook"]
loginWithFacebook.tap()
// Sleep to give the alert time to show up
sleep(2)
// Interact with the app to get the above monitor to fire
app.swipeUp()
}
}