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

Swift单元测试中如何配置全局实例供多测试用例复用

XCTest 全局共享实例与多用例期望适配方案

一、全局实例单次初始化实现

XCTest 原生提供测试生命周期钩子,不需要在每个测试用例里重复调用初始化方法:

  • 类级别setUp()方法会在当前测试类所有用例执行前仅运行一次,适合创建蓝牙管理器、灯控制器这类初始化/连接成本高的共享实例,对应声明的共享变量需用static修饰,保证跨用例访问。
  • 因为蓝牙连接是异步流程,需要在首次初始化时单独等待连接完成,再执行后续测试用例,避免用例启动时实例还未就绪。

对应代码实现:

// 测试类顶部声明静态共享变量
static var sharedLightController: HueBTLightController?
static var sharedBluetoothManager: CBCentralManager? // 蓝牙管理器实例也建议设为静态共享
static var isConnected: Bool = false
static var initConnectExpectation: XCTestExpectation?

override class func setUp() {
    super.setUp()
    // 以下初始化逻辑整个测试生命周期仅执行一次
    let btLight = HueBTLight(identifier: "093FB4B8-82E6-A124-3888-4F25C19CFDB7")
    btLight.name = "Hue Bloom"
    sharedLightController = HueBTLightController(light: btLight)
    sharedLightController?.delegate = self
    sharedBluetoothManager?.selectLight(sharedLightController!.light!)
    sharedBluetoothManager?.delegate = sharedLightController

    // 等待首次连接完成再开始执行测试用例
    initConnectExpectation = XCTestExpectation(description: "Wait for light first connect")
    wait(for: [initConnectExpectation!], timeout: 25)
}

如果担心类级wait的API兼容问题,也可以在实例级setUp()里加静态标记判断,保证初始化逻辑仅执行一次,效果一致。

二、代理回调适配多测试用例期望

你当前的问题是把不同用例的期望硬编码为全局变量,回调时无差别触发,会导致期望提前完成、空指针等异常。解决方案是统一用单个当前期望指针持有当前运行用例的等待期望,回调时仅触发当前存在的期望:

  1. 测试类内声明单个当前用例期望变量,不需要为每个用例单独声明期望变量:
var currentExpectation: XCTestExpectation?
  1. 每个测试用例内自行创建期望,赋值给当前期望变量,直接使用已经初始化完成的共享实例即可,不需要重复调用初始化方法:
func testColorModel() {
    currentExpectation = expectation(description: "Testing setting color")
    // 写入测试颜色的业务逻辑,触发代理回调
    wait(for: [currentExpectation!], timeout: 20)
}

func testSetOn() {
    currentExpectation = expectation(description: "Testing setting on and off")
    // 写入测试开关的业务逻辑,触发代理回调
    wait(for: [currentExpectation!], timeout: 20)
}
  1. 修改代理回调逻辑,优先处理初始化阶段的连接期望,再处理当前用例的期望,不要硬编码触发多个固定期望:
func didConnectLightController(_ object: CDHueBTLightController, success: Bool) {
    Self.isConnected = success
    // 首次初始化阶段的连接回调
    if let initExp = Self.initConnectExpectation {
        initExp.fulfill()
        Self.initConnectExpectation = nil
        return
    }
    // 普通测试用例的回调
    currentExpectation?.fulfill()
    currentExpectation = nil
}
  1. 补充用例收尾逻辑,避免上一个用例的状态残留:
override func tearDown() {
    currentExpectation = nil
    // 可在此处重置灯的状态,避免上一个用例的修改影响后续用例
    super.tearDown()
}

注意事项

  • 不要依赖XCTest的用例执行顺序编写逻辑,XCTest默认按方法名的字典序执行用例,后续版本可能调整执行顺序。
  • 涉及硬件交互的测试如果出现偶现失败,可以适当拉长超时时间,或者在每个用例执行前增加简单的连接状态校验。
  • 如果测试用例之间对实例状态的修改会互相干扰,不建议使用全局共享实例,还是在每个用例的setUp里初始化、tearDown里销毁更稳定。

内容的提问来源于stack exchange,提问作者Nasim Rony

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 05:24:34