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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 12:15:32