【问题标题】:XCTestCase: UIApplication.shared.keyWindow returns nilXCTestCase:UIApplication.shared.keyWindow 返回 nil
【发布时间】:2017-06-11 03:13:46
【问题描述】:

当我打电话时

UIApplication.shared.keyWindow

尝试在我的测试类中设置根视图控制器,键窗口返回 nil。为什么会这样?

我是这样设置故事板的:

let testBoard = UIStoryboard(name: "TestStoryboard", bundle: Bundle(for: type(of: self)))
let vc = testBoard.instantiateViewController(withIdentifier: "TestController")

UIApplication.shared.keyWindow?.rootViewController = vc

_ = vc.view
vc.viewDidLoad()

【问题讨论】:

  • 不确定,但我认为这是因为窗口默认为nil 而您还没有实例化窗口。见here
  • 顺便说一句,你为什么使用keyWindow?尝试改用 window 看看是否可行,或者您正在构建 macOS 应用程序?
  • 我查看了链接,但在任何地方都没有看到解决方案。我无法设置 UIApplication.shared.keyWindow 属性,因为它是只读的。如何实例化窗口?
  • 再次。你能告诉我为什么你使用keyWindow而不是window吗?它是一个mac应用程序吗? (FWIW 我没有回答,因为我还在学习 Swift + TDD,只是用有限的知识发表评论:D)
  • 所以我不知道 keyWindow 是:这个属性保存了最近发送 makeKeyAndVisible() 消息的 windows 数组中的 UIWindow 对象。 AppDelegate 带有一个pre-build 可选的窗口属性,但开发者可以添加更多。

标签: ios swift swift3 xctest


【解决方案1】:

创建一个窗口并分配视图控制器。

let testBoard = UIStoryboard(name: "TestStoryboard", bundle: Bundle(for: type(of: self)))
let vc = testBoard.instantiateViewController(withIdentifier: "TestController")

let window = UIWindow()
window.rootViewController = vc
window.makeKeyAndVisible()

vc.view.layoutIfNeeded()

// Add test here

我注意到,在那之后你还打电话给vc.view 和viewDidLoad。我建议只访问视图以加载它而不是隐式调用viewDidLoad - 我个人使用vc.view.layoutIfNeeded()

根据您实际需要测试的内容,对我而言,很少需要将视图控制器分配给窗口本身。通常,您只需创建视图控制器的实例即可逃脱,如果您正在测试任何 UI 代码,还可以确保填充视图。

我需要将视图控制器分配给窗口的唯一一次是在测试诸如导航之类的东西时,我想断言另一个视图控制器由于某些操作而以模态方式呈现。

【讨论】:

  • 你完全正确,这就是我正在做的。我正在尝试展示一个视图控制器,所以我必须设置窗口。那么如何将视图控制器分配给窗口,以便呈现其他视图?
  • 难道你不能从 AppDelegate 实例化窗口吗?
  • 我正在运行单元测试,因此我试图避免更改测试文件外部的任何代码。
  • 我已经更新了代码,使其更适用于您的示例。
  • 是的,您还应该提到@Honey 在他的评论中提到的内容:此属性将 UIWindow 对象保存在最近发送 makeKeyAndVisible() 消息的 windows 数组中。 具体来说关于 makeKeyAndVisibile 的部分
【解决方案2】:

在需要访问真实 UIWindow 但没有自己的测试应用程序的库中测试 UI 时,我遇到了类似的问题。

我的解决方法是添加一个虚拟的 iOS 单一视图目标,然后将其分配为单元测试主机应用程序。

这当然会为您的测试代码提供一个 UIApplication 单例,其中包含委托和窗口供您使用。

import XCTest
import UIKit

class ExampleTestCase: XCTestCase {
    var rootViewController: UIViewController!

    override func setUp() {
        guard let mainWindow = UIApplication.shared.delegate?.window,
            let window = mainWindow else {
                fatalError("Unable to find window")
        }
        rootViewController = UIViewController()
        window.rootViewController = rootViewController

        setupView()

        window.makeKeyAndVisible()
        RunLoop.current.run(until: Date())
    }
    func setupView() {
        rootViewController.view.backgroundColor = .red
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        label.text = "Hello!"
        label.textColor = .white
        label.font = UIFont.systemFont(ofSize: 25)
        //Do something with window
        rootViewController.view.addSubview(label)
        label.translatesAutoresizingMaskIntoConstraints = false
        label.centerXAnchor.constraint(equalTo: label.superview!.centerXAnchor).isActive = true
        label.centerYAnchor.constraint(equalTo: label.superview!.centerYAnchor).isActive = true
    }

    func testExample() {
        print("Im a useless test!")
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-18
    • 2019-06-20
    • 2017-12-16
    • 2014-09-27
    • 2014-12-29
    • 2019-03-14
    • 2016-06-16
    相关资源
    最近更新 更多