Swift Combine Future测试中线程不一致问题求助
XCTest中Future的sink闭包线程一致性问题及解决方案
问题概述
测试中期望Future的sink闭包与Future完成闭包运行在同一非主线程,但该预期稳定性极差——每运行100次测试约有8次失败。此问题仅在使用Future时出现,PassthroughSubject无异常。已提交XCTest bug工单FB13358522,可在Xcode 14.2 (14C18)和Xcode 15 (15A240d)版本中复现。
复现代码
let expectation = expectation(description: "") let future = Future<Void, Error> { future in DispatchQueue.global().async { future(.success(())) } } future.sink(receiveCompletion: { value in }, receiveValue: { _ in XCTAssertNotEqual(Thread.current, Thread.main) expectation.fulfill() }) .store(in: &cancellables) waitForExpectations(timeout: 1.0)
临时解决方案
在DispatchQueue.global().async闭包内添加print语句,可临时规避问题:
DispatchQueue.global().async { print(Thread.current) future(.success(())) }
更优规避方法
方法1:显式指定sink的接收队列
通过receive(on:)操作符强制sink闭包在全局队列执行,确保断言稳定通过:
let expectation = expectation(description: "") let future = Future<Void, Error> { future in DispatchQueue.global().async { future(.success(())) } } future .receive(on: DispatchQueue.global()) // 显式指定接收队列 .sink(receiveCompletion: { value in }, receiveValue: { _ in XCTAssertNotEqual(Thread.current, Thread.main) expectation.fulfill() }) .store(in: &cancellables) waitForExpectations(timeout: 1.0)
方法2:固化Future完成回调的线程上下文
在调用future(.success(()))前,通过全局队列同步执行绑定线程,避免调度异常:
let future = Future<Void, Error> { future in DispatchQueue.global().async { DispatchQueue.global().sync { future(.success(())) } } }
PassthroughSubject正常示例代码
对比之下,PassthroughSubject无此线程异常,代码示例:
let subject = PassthroughSubject<Void, Error>() subject.sink(receiveCompletion: { value in XCTAssertNotEqual(Thread.current, Thread.main) expectation.fulfill() }) .store(in: &cancellables) DispatchQueue.global().async { subject.send(()) subject.send(completion: .finished) }
内容的提问来源于stack exchange,提问作者omarzl
相关产品推荐
相关产品推荐

