Swift中如何Mock ClassB的doSomething方法以测试并发场景?
解决方案:Mock ClassB 实现延迟响应
要实现Mock ClassB的doSomething方法并模拟延迟返回,核心是通过依赖注入让ClassA可替换为Mock实现,而非硬编码实例化ClassB。以下是具体实现步骤:
1. 重构ClassA,支持依赖注入
修改ClassA,新增工厂方法属性用于创建ClassB实例,让测试时可替换为Mock逻辑:
public class ClassA: ClassAProtocol { static var instance: ClassA? = nil static let initQueue = DispatchQueue(label: "queue") static let semaphore = DispatchSemaphore(value: 1) // 新增工厂属性,默认返回ClassB实例,测试时可替换 static var classBFactory: (String) -> ClassBProtocol = { key in ClassB(key: key) } @objc public static func getSomething( withKey key: String, completionHandler: @escaping (ClassA?, Error?) -> Void ) { ClassA.initQueue.async { ClassA.semaphore.wait() DispatchQueue.main.async { if let objectA = ClassA.instance { ClassA.semaphore.signal() completionHandler(objectA, nil) return } // 通过工厂方法创建ClassB,而非直接初始化 let objectB = ClassA.classBFactory(key) objectB.doSomething { response, error in guard let response = response else { ClassA.semaphore.signal() completionHandler(nil, error) return } let objectA = ClassA() ClassA.instance = objectA ClassA.semaphore.signal() completionHandler(objectA, nil) } } } } }
2. 创建MockClassB实现延迟逻辑
定义遵循ClassBProtocol的Mock类,在doSomething中实现0.5秒延迟返回的逻辑:
class MockClassB: ClassBProtocol { private let key: String init(key: String) { self.key = key } func doSomething(completionHandler: @escaping (ClassBProtocol?, Error?) -> Void) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { completionHandler(MockClassB(key: self.key), nil) } } }
3. 在测试用例中注入MockClassB
修改测试方法,替换ClassA的工厂属性为MockClassB,并修复原测试中缺少withKey参数的问题:
func testGetSomethingReturnsSameInstance() { // 重置单例和工厂,保证测试独立性 ClassA.instance = nil ClassA.classBFactory = { key in MockClassB(key: key) } let expectation1 = self.expectation(description: "getSomething 1 completed") let expectation2 = self.expectation(description: "getSomething 2 completed") let expectation3 = self.expectation(description: "getSomething 3 completed") var client1: ClassA? var client2: ClassA? var client3: ClassA? // 补充withKey参数 ClassA.getSomething(withKey: "testKey") { (client, error) in client1 = client expectation1.fulfill() } ClassA.getSomething(withKey: "testKey") { (client, error) in client2 = client expectation2.fulfill() } ClassA.getSomething(withKey: "testKey") { (client, error) in client3 = client expectation3.fulfill() } waitForExpectations(timeout: 10) { (error) in // 用XCTAssertIdentical验证引用相等(同一实例) XCTAssertIdentical(client1, client2) XCTAssertIdentical(client2, client3) XCTAssertIdentical(client1, client3) // 测试后恢复默认工厂,避免影响其他测试 ClassA.classBFactory = { key in ClassB(key: key) } } }
关键说明
- 工厂方法实现依赖注入,既保留原有业务逻辑,又能在测试时灵活替换实现。
- 使用
XCTAssertIdentical而非XCTAssertEqual,确保验证的是同一实例(引用相等),而非值相等。 - 测试前后重置单例和工厂属性,避免不同测试用例间的状态干扰。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

