使用 UIWindow() 进行单元测试时出现 NSInternalConsistencyException

NSInternalConsistencyException when Unit Testing with UIWindow()

我一直在使用 following post 指导如何从与特定 UIViewController 无关的代码中显示 UIAlertController。现在,我想对这段代码进行单元测试:

func showAlert(alert: UIAlertController, animated: Bool, completion: (()->Void)?) 
{
    let alertWindow = UIWindow(frame: UIScreen.mainScreen().bounds)

    // Keep a strong refence to the window. 
    // We manually set this to nil when the alert is dismissed
    self.alertWindow = alertWindow

    alertWindow.rootViewController = UIViewController()        
    if let currentTopWindow = UIApplication.sharedApplication().windows.last {
        alertWindow.windowLevel = currentTopWindow.windowLevel + 1
    }
    else {
        // This case only happens during unit testing
        Logger.trace(ICELogLevel.Error, category: .Utility, message: "The application doesn't have a window being displayed!")
    }

    // preload the viewController for unit testing 
    // (see https://www.natashatherobot.com/ios-testing-view-controllers-swift/ )
    let _ = alertWindow.rootViewController?.view
    alertWindow.makeKeyAndVisible()

    alertWindow.rootViewController!.presentViewController(self.alertController, animated: animated, completion: completion)
}

然而,当 运行 单元测试时,在 alertWindow.makeKeyAndVisible() 行,我得到一个 NSInternalInconsistencyException: props must have a valid clientID

此代码在应用程序代码中有效,我不想使用模拟 UIWindow 等,因为我想验证警报是否实际上显示在一个 真实 UIWindow.

关于我们如何在单元测试中使用 UIWindows() 的任何指南?我做错了什么?

我们还没有解决 UIWindow 是否可以设置为在单元测试环境中工作的问题,但正如@matt 的评论让我思考的那样,这可能不是我们首先需要进行单元测试,原因如下:

  1. 我们已经通过警报场景 UI 进行了 运行 测试。如果构建中断显示警报,我们会很快注意到
  2. 对于其余的单元测试目的,我们可以简单地存根此方法以立即调用完成回调。
  3. 根据问题的最初动机,似乎没有办法在单元测试中正确使用 UIWindow 对象。

问题是 makeKeyAndVisible 调用内部需要 运行 UIApplication 实例的代码。这就是为什么在测试框架时抛出异常而不是在测试应用程序时抛出异常的原因。应用程序测试将测试包注入 运行 应用程序。简单的解决方法是从:

alertWindow.makeKeyAndVisible()

alertWindow.isHidden = false

这有效并允许使用 window 单独测试视图控制器。缺点当然是这与 makeKeyAndVisible 不同 - 它没有触发异常的副作用。

另一种策略是专门创建一个新的空应用程序目标来支持测试。将被测框架嵌入其中,并将其用作单元测试的主机应用程序。