向下转换/子类 UIViewController 用于单元测试中的模拟
Posted
技术标签:
【中文标题】向下转换/子类 UIViewController 用于单元测试中的模拟【英文标题】:Downcast / subclass UIViewController for mock in unit test 【发布时间】:2019-04-08 21:32:05 【问题描述】:我有一个单元测试,我想创建一个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
?
【问题讨论】:
【参考方案1】:因为情节提要中的对象实际上是编码对象,所以它们不能被解码并转换为其他任何东西。这是使用故事板的一个缺点。你在故事板中的内容就是你得到的东西。
如果可以,请使用基于 XIB 的视图控制器而不是基于情节提要的视图控制器。使用 XIB(以及编程视图控制器),测试可以实例化子类。
如果没有,那么您需要为视图控制器引入一个后门。这将是不幸的,因为这意味着将测试代码混合到您的生产代码中。
【讨论】:
这很好地解释了为什么它不能正常工作。【参考方案2】:@Jon Reid 的answer 很好地总结了使用故事板的局限性。
如果您的最终目标是验证被测 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 test double 调用了呈现某些内容的导航委托方法。 如果您对这种方法感到好奇,很乐意提供更多详细信息。
【讨论】:
【参考方案3】:@mokagios answer 应该可以工作。我更喜欢另一种方法。
func test_presentationOfViewController()
// Arrange
let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
window.rootViewController = sut
window.makeKeyAndVisible()
// Act
sut.presentNext()
// Assert
XCTAssertTrue(sut.presentedViewController is DetailViewController)
【讨论】:
以上是关于向下转换/子类 UIViewController 用于单元测试中的模拟的主要内容,如果未能解决你的问题,请参考以下文章