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

为HealthKit的requestAuthorization编写单元测试遇超时错误求助

问题

通过生成HKHealthStore的Mock对象编写requestAuthorization方法的单元测试时,出现异步等待超时错误:2秒内未满足预期「Successfully tested requestAuthorization by returning true.」。相关代码如下:

业务代码

func requestAuthorization(completion: @escaping(Bool?, HealthError?) -> Void) {
        self.healthStore?.requestAuthorization(toShare: self.allTypes as? Set<HKSampleType>, read: self.allTypes, completion: { authorized, error in
            if error != nil {
                print("ERROR: \(error)")
                completion(nil, .unableToAuthorizeAccess)
            }
            completion(authorized, nil)
        })
}

测试代码

func testRequestAuthorization_CanReturnTrue() {
        let expectation = expectation(description: "Successfully tested requestAuthorization by returning true.")
        sut?.requestAuthorization { authorized, error in
            if error != nil {
                print(error!)
            }
            guard let authorized = authorized else { return }
            XCTAssertTrue(authorized)
            expectation.fulfill()
        }
        wait(for: [expectation], timeout: 2)
}

Mock对象重写方法

override func requestAuthorization(toShare typesToShare: Set<HKSampleType>?, read typesToRead: Set<HKObjectType>?, completion: @escaping (Bool, Error?) -> Void) {
        invokedRequestAuthorization = true
        invokedRequestAuthorizationCount += 1
        invokedRequestAuthorizationParameters = (typesToShare, typesToRead)
        invokedRequestAuthorizationParametersList.append((typesToShare, typesToRead))
        if let result = stubbedRequestAuthorizationCompletionResult {
            print("RESULT: \(result)")
            completion(result.0, result.1)
        }
}
问题分析

超时的核心原因有两个:

  • Mock的返回结果未设置:Mock的requestAuthorization方法仅在stubbedRequestAuthorizationCompletionResult有值时才会调用completion闭包,若未赋值,闭包永远不会执行,导致测试预期无法被满足,最终超时。
  • 业务代码存在重复调用completion的问题:当error存在时,业务代码先调用一次completion(nil, .unableToAuthorizeAccess),之后又执行completion(authorized, nil),逻辑混乱可能影响测试闭包的正常执行。
解决方案

1. 给Mock对象设置返回结果

在测试方法中,提前给Mock的stubbedRequestAuthorizationCompletionResult赋值,确保它能触发completion闭包:

func testRequestAuthorization_CanReturnTrue() {
        let expectation = expectation(description: "Successfully tested requestAuthorization by returning true.")
        
        // 给Mock实例设置授权成功的返回结果
        let mockHealthStore = yourMockHealthStoreInstance // 替换为你实际的Mock对象实例
        mockHealthStore.stubbedRequestAuthorizationCompletionResult = (true, nil)
        sut?.healthStore = mockHealthStore
        
        sut?.requestAuthorization { authorized, error in
            if let error = error {
                print(error)
            }
            guard let authorized = authorized else {
                XCTFail("授权状态返回nil")
                expectation.fulfill()
                return
            }
            XCTAssertTrue(authorized)
            expectation.fulfill()
        }
        wait(for: [expectation], timeout: 2)
}

2. 修复业务代码的completion重复调用

在业务代码中,当捕获到error时,调用completion后直接返回,避免后续重复执行:

func requestAuthorization(completion: @escaping(Bool?, HealthError?) -> Void) {
        self.healthStore?.requestAuthorization(toShare: self.allTypes as? Set<HKSampleType>, read: self.allTypes, completion: { authorized, error in
            if let error = error {
                print("ERROR: \(error)")
                completion(nil, .unableToAuthorizeAccess)
                return // 新增return,终止后续代码执行
            }
            completion(authorized, nil)
        })
}

3. 测试中增加异常兜底处理

在测试的闭包内,如果authorized为nil,主动标记测试失败并完成预期,避免静默等待超时:

guard let authorized = authorized else {
    XCTFail("未获取到有效授权状态")
    expectation.fulfill()
    return
}

内容的提问来源于stack exchange,提问作者Shak Feizi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 11:45:29