我有一个单元测试,我想创建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
}
还有一个Storyboard类。
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
?
答案 0 :(得分:1)
由于情节提要中的对象实际上是已编码的对象,因此无法将其解码并转换为其他任何对象。这是使用情节提要的缺点。放在情节提要中的就是您得到的内容。
如果可以,请使用基于XIB的视图控制器,而不要使用基于情节提要的视图控制器。使用XIB(以及编程视图控制器),测试可以实例化子类。
如果没有,那么您需要为视图控制器引入后门。这将是不幸的,因为这意味着将测试代码混入您的生产代码中。
答案 1 :(得分:1)
@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调用了呈现某些内容的导航委托方法时才可以。 如果您对此方法感到好奇,很乐意提供更多详细信息。
答案 2 :(得分:0)
@mokagio的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)
}