如何不用XCUITest在XCTest中构建多层ViewController层级
问题说明
不使用XCUITest开展ViewController层级测试,核心诉求是规避XCUITest远高于普通XCTest的执行耗时。实际测试中始终无法创建深度超过1层的ViewController层级,可复现问题的测试代码如下:
class VCHierarchyTests: XCTestCase { func test_CanMakeViewControllerHierarchy() { let baseVC = UIViewController() let vcToPresent = UIViewController() let vcOnTopOfThat = UIViewController() let vcOnTopOfThatOnTopOfThat = UIViewController() newKeyWindowWith(root: baseVC) baseVC.present(vcToPresent, animated: false) vcToPresent.present(vcOnTopOfThat, animated: false) vcOnTopOfThat.present(vcOnTopOfThatOnTopOfThat, animated: false) // 断言通过 XCTAssertNotNil(baseVC.presentedViewController) // 断言失败 XCTAssertNotNil(vcToPresent.presentedViewController) // 断言失败 XCTAssertNotNil(vcOnTopOfThat.presentedViewController) } private func newKeyWindowWith(root viewController: UIViewController){ let window = UIWindow(frame: UIScreen.main.bounds) window.makeKeyAndVisible() window.rootViewController = viewController } }
问题根因
原有代码无法构建多层级的原因有两个:
newKeyWindowWith方法中创建的UIWindow是局部变量,出方法作用域后就会被ARC回收,没有被测试用例生命周期持有,导致视图层级状态异常- 直接串行同步调用
present方法时,前一个控制器的呈现流程还未完成(未完成挂载到窗口、生命周期方法未执行完毕),UIKit会忽略后续的present请求,无法形成多层呈现链路
可行实现方案
完全基于普通XCTest即可实现多层ViewController层级构建,不需要引入XCUITest,修正后的可运行代码如下:
class VCHierarchyTests: XCTestCase { // 持有测试窗口的强引用,避免提前释放 private var testWindow: UIWindow! func test_CanMakeViewControllerHierarchy() { let baseVC = UIViewController() let vcToPresent = UIViewController() let vcOnTopOfThat = UIViewController() let vcOnTopOfThatOnTopOfThat = UIViewController() newKeyWindowWith(root: baseVC) // 等待前一个present流程完成后,再触发下一次present let allVCPresentedExpectation = expectation(description: "所有控制器呈现完成") baseVC.present(vcToPresent, animated: false) { vcToPresent.present(vcOnTopOfThat, animated: false) { vcOnTopOfThat.present(vcOnTopOfThatOnTopOfThat, animated: false) { allVCPresentedExpectation.fulfill() } } } wait(for: [allVCPresentedExpectation], timeout: 1) // 所有断言均可正常通过 XCTAssertNotNil(baseVC.presentedViewController) XCTAssertNotNil(vcToPresent.presentedViewController) XCTAssertNotNil(vcOnTopOfThat.presentedViewController) } private func newKeyWindowWith(root viewController: UIViewController){ testWindow = UIWindow(frame: UIScreen.main.bounds) testWindow.makeKeyAndVisible() testWindow.rootViewController = viewController } }
该实现的执行效率和常规XCTest单元测试完全一致,没有XCUITest的启动额外耗时。如果需要构建更深的控制器层级,只需要按照present完成回调嵌套的逻辑顺延即可,也可以封装通用的present串行工具方法减少冗余代码。
内容的提问来源于stack exchange,提问作者Le Mot Juiced
相关产品推荐
相关产品推荐

