首页 > 解决方案 > 向下转换/子类 UIViewController 用于单元测试中的模拟

问题描述

我有一个单元测试,我想创建一个 的子类版本UIViewController,例如Test1ViewController. 具体来说,我想重写present这个类的方法。

我有一个视图控制器扩展,它根据其类名实例化视图控制器。

public class func instanceFromStoryboard<T>(storyboard: Storyboard) -> T {
    return UIStoryboard(name: storyboard.rawValue, bundle: nil).instantiateViewController(withIdentifier: String(describing: T.self)) as! T
}

还有一个故事板课程。

public enum Storyboard: String {
    case main = "Main"
}

在我的单元测试中,我从Test1ViewController.

class Test2ViewController: Test1ViewController {
    var presented: Bool = false
    override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
        presented = true
    }
}

如何使用我的扩展方法从情节提要中检索视图控制器,然后向下转换/子类化为Test2ViewController

标签: iosswiftunit-testingxctest

解决方案


@Jon Reid回答很好地总结了使用情节提要的局限性。

如果您的最终目标是验证UIViewController被测对象是否呈现了某些东西,您是否考虑过检查该presentedViewController属性?

// Create an asynchronous expectation to verify the view controller has presented
// something.
_ = expectation(
  for: NSPredicate(
    block: { input, _ -> Bool in
      guard let viewController = input as? UIViewController else { return false }
        // If you care about the type of the presented view controller you could
        // use `is` here to verify it
        return viewController.presentedViewController != nil
      }
    ),
    evaluatedWith: viewControllerUnderTest,
    handler: .none
)

viewControllerUnderTest.doSomething()

waitForExpectations(timeout: 1, handler: nil)

另一种方法,它需要更多的工作,但随着时间的推移,通过使您的视图控制器更容易在导航流中移动而得到回报,是对所有演示文稿使用委托。

在测试中,您不会检查是否已呈现某些内容,但仅当使用Spy 测试 double调用了呈现某些内容的导航委托方法时。如果您对这种方法感到好奇,很高兴提供更多详细信息。


推荐阅读