You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何不用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 19:18:40