【问题标题】:XCUITesting for permission popup: alert appears, but UIInterruptionMonitor does not fireXCUITesting 权限弹出窗口:出现警报,但 UIInterruptionMonitor 未触发
【发布时间】:2016-06-18 15:33:22
【问题描述】:

我想写一个这样的测试:

当我的应用程序进入某个窗格时,它应该请求使用相机的权限。

我想测试窗格是否出现。我正在使用 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 模拟器。

【问题讨论】:

    标签: ios swift xcode-ui-testing xcode7.2


    【解决方案1】:

    所以pancake's answer 为我工作。但是,我认为它可以简化。显示系统警报时似乎确实存在某种奇怪的死锁或竞争条件。

    而不是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()
        }
    }
    

    【讨论】:

      【解决方案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)
      

      【讨论】:

      • app.tap 步骤在 iOS 12 / Xcode 10.1 上似乎没有必要
      • 在 IOS 13 beta 上似乎确实有必要。
      【解决方案3】:

      我也注意到了这个问题。似乎中断处理程序是异步运行的,并且无法断言它们是否被调用。等待期望似乎也完全阻止了中断监视器的运行。看起来系统正在等待期望实现,而期望正在等待中断监视器触发。典型的死锁案例。

      但是,我发现了一个相当古怪的解决方案,它使用基于 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() 以某种方式允许运行中断监视器,即使测试用例正在等待预期。

      我希望这对你和我一样有效!

      【讨论】:

      • 我不喜欢这种方法,但是再次回到类似的问题后,我确实让它工作了。
      • 我也不喜欢:D
      • 我注意到不需要跟踪对话,只要在期望的谓词闭包中点击应用程序就会调用中断监视器闭包,因此它可以立即返回true
      • 此解决方案适用于我在 Xcode 11.3 上的测试 50/50
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-22
      相关资源
      最近更新 更多