使用 UIWindow() 进行单元测试时出现 NSInternalConsistencyException
Posted
技术标签:
【中文标题】使用 UIWindow() 进行单元测试时出现 NSInternalConsistencyException【英文标题】:NSInternalConsistencyException when Unit Testing with UIWindow() 【发布时间】:2016-07-13 23:43:51 【问题描述】:我一直使用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 入侵到视图层次结构中是没有意义的。 — 但是,为了更有帮助:这真的是您想要进行单元测试的东西吗?这听起来更像是 UI 测试的候选者。 我不认为这是黑客行为。请参阅this post 了解添加 UIWindows 的意义所在。我认为这是其中之一——我们希望这个警报“浮动”在其他 UI 元素之上。此外,我的问题中链接的帖子表明这是 Apple 对 UIAlertController 问题的内部解决方案 Apple 的 自己的 警报(例如,来电)当然是窗口。但这在应用程序之外。这并不意味着 你 应该这样做,这也不是 UIAlertController 的工作方式。看,如果你愿意,我可以给你一个完全正常的非 hacky 非窗口方式来使自定义警报“浮动”在其他 UI 元素之上。但当然,这完全取决于你想走哪条路。 嗯,是的,我正在关注问题开始时链接的帖子。如果有更好的方法可以做到这一点,我会全力以赴 github.com/mattneub/custom-alert-view-iOS7 演示了正确的现代方式 - 浮动警报的位置、大小和内容完全取决于您。碰巧我试图让它看起来像 UIAlertController 的视图,但无论如何你都不需要这样做。 【参考方案1】:问题在于makeKeyAndVisible
调用的代码在内部需要一个正在运行的 UIApplication 实例。这就是为什么在测试框架而不是在测试应用程序时抛出异常的原因。应用程序测试将测试包注入到正在运行的应用程序中。简单的解决方法是从:
alertWindow.makeKeyAndVisible()
到
alertWindow.isHidden = false
这可行,并允许一个视图控制器与一个窗口隔离地进行测试。缺点当然是这与makeKeyAndVisible
不同相同 - 它没有触发异常的副作用。
另一种策略是专门创建一个新的空应用程序目标来支持测试。将被测框架嵌入其中,并将其用作单元测试的主机应用程序。
【讨论】:
我有一个框架来测试这些与 UIKIt 相关的副作用是否已经发生,我目前正在尝试将框架嵌入到一个虚拟应用程序中。您以前是否尝试过这样做并且可以提供任何指导?【参考方案2】:我们还没有解决 UIWindow 是否可以设置为在单元测试环境中工作的问题,但正如 @matt 的评论让我想到的那样,这可能不是我们首先需要进行单元测试的东西,原因如下:
-
我们已经有了在警报场景中运行的 UI 测试。如果构建中断正在显示的警报,我们会很快注意到
对于其余的单元测试目的,我们可以简单地存根此方法以立即调用完成回调。
根据问题的最初动机,似乎没有办法在单元测试中正确使用 UIWindow 对象。
【讨论】:
以上是关于使用 UIWindow() 进行单元测试时出现 NSInternalConsistencyException的主要内容,如果未能解决你的问题,请参考以下文章
Play 2.2:使用 Play Caching (Scala) 对代码进行单元测试时出现问题
获取错误:在角度 js 中进行单元测试时出现 $injector:modulerr 模块错误
ReferenceError:未定义窗口。当我通过 jest 运行 npm test 进行单元测试时出现此错误
为啥在尝试对整个 Spring Batch Job 进行单元测试时出现此错误?没有可用的“org.springframework.batch.core.Job”类型的合格bean