我想写一个这样的测试:
当我的应用转到某个窗格时,它应该请求使用相机的权限。
我想测试窗格是否出现。我正在使用XC的内置UITest框架来执行此操作。根据我在谷歌和这里发现的情况,似乎我应该做以下事情:
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,它正确地导致出现“给予权限”弹出窗口。但是,我希望这会触发中断监视器。但是,没有发现这种中断,也不会实现。
我在某个地方读到了对话框出现后你应该app.tap()
。但是,当我这样做时,它会单击“允许”按钮。对话框消失,仍然没有处理中断。
是否有某种方式将权限对话框视为“警报”或无法处理?我甚至进去并用一个只看app.alerts
的东西替换了中断位,但结果是空的,即使我正在看模拟器中的弹出窗口。
谢谢!我正在为iPhone 6s使用Xcode7.2,iOS 9.2模拟器。
答案 0 :(得分:5)
我也注意到了这个问题。看起来中断处理程序是异步运行的,并且没有办法断言它们是否被调用。等待期望似乎也阻止了中断监视器的运行。看起来系统正在等待期望实现并期望等待中断监视器触发。一个经典的死锁案例。
但是,我找到了一个使用基于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()
以某种方式允许运行中断监视器,即使测试用例正在等待期望。
我希望这对你有用,就像对我一样!
答案 1 :(得分:2)
pancake的答案有效,但只有在第一次测试应用程序时才会这样。如果该应用程序之前已在同一模拟器中进行了测试,则该权限将已授予该应用程序,因此警报将永远不会出现,并且测试将失败。
我的方法是等待应该出现在应用程序中的元素,而不是等待处理警报对话框。如果警告对话框在应用程序上,则应用程序的元素将不会“存在”,因为它无法访问/可点击。
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)
答案 2 :(得分:1)
所以pancake's answer为我工作。但是,我认为它可以简化。出现系统警报时,确实出现了某种奇怪的死锁或争用情况。
在显示系统警报之后和尝试NSPredicate
之前,我只是使用sleep(2)
而不是XCUIApplication().tap()
的期望。
我还决定使用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()
}
}