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()里加静态标记判断,保证初始化逻辑仅执行一次,效果一致。
二、代理回调适配多测试用例期望
你当前的问题是把不同用例的期望硬编码为全局变量,回调时无差别触发,会导致期望提前完成、空指针等异常。解决方案是统一用单个当前期望指针持有当前运行用例的等待期望,回调时仅触发当前存在的期望:
- 测试类内声明单个当前用例期望变量,不需要为每个用例单独声明期望变量:
var currentExpectation: XCTestExpectation?
- 每个测试用例内自行创建期望,赋值给当前期望变量,直接使用已经初始化完成的共享实例即可,不需要重复调用初始化方法:
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) }
- 修改代理回调逻辑,优先处理初始化阶段的连接期望,再处理当前用例的期望,不要硬编码触发多个固定期望:
func didConnectLightController(_ object: CDHueBTLightController, success: Bool) { Self.isConnected = success // 首次初始化阶段的连接回调 if let initExp = Self.initConnectExpectation { initExp.fulfill() Self.initConnectExpectation = nil return } // 普通测试用例的回调 currentExpectation?.fulfill() currentExpectation = nil }
- 补充用例收尾逻辑,避免上一个用例的状态残留:
override func tearDown() { currentExpectation = nil // 可在此处重置灯的状态,避免上一个用例的修改影响后续用例 super.tearDown() }
注意事项
- 不要依赖XCTest的用例执行顺序编写逻辑,XCTest默认按方法名的字典序执行用例,后续版本可能调整执行顺序。
- 涉及硬件交互的测试如果出现偶现失败,可以适当拉长超时时间,或者在每个用例执行前增加简单的连接状态校验。
- 如果测试用例之间对实例状态的修改会互相干扰,不建议使用全局共享实例,还是在每个用例的setUp里初始化、tearDown里销毁更稳定。
内容的提问来源于stack exchange,提问作者Nasim Rony
相关产品推荐
相关产品推荐

