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

Swift中async/await结合TestScheduler测试及Async Algorithms测试探讨

TestScheduler与async/await结合及Swift Async Algorithms时间相关运算符测试方案

一、Pointfree Combine Schedulers的TestScheduler可与async/await结合实现虚拟时间推进

完全可以实现,核心是将TestScheduler作为调度器注入到async代码的执行上下文,替代默认的全局调度器,从而手动控制时间线,彻底避免真实等待。

示例代码:

import CombineSchedulers
import XCTest

final class AsyncAwaitSchedulerTests: XCTestCase {
    func testDebouncedAsyncTask() async {
        let testScheduler = TestScheduler()
        var receivedResult: String?
        
        // 用TestScheduler包裹async任务的执行
        await testScheduler.run {
            let delayedTask = Task {
                try await testScheduler.sleep(for: .seconds(1))
                return "completed"
            }
            receivedResult = await delayedTask.value
        }
        
        // 未推进时间时,任务未完成
        XCTAssertNil(receivedResult)
        
        // 手动推进1秒虚拟时间
        testScheduler.advance(by: .seconds(1))
        
        // 任务此时已完成,可验证结果
        XCTAssertEqual(receivedResult, "completed")
    }
}

原理是TestScheduler接管了所有依赖时间的异步操作的时间源,sleep、定时器等操作都会基于虚拟时间执行,而非真实时间。

二、Swift Async Algorithms中时间相关运算符的测试方案

Swift Async Algorithms的debounce、throttle等运算符支持传入自定义Scheduler参数,结合TestScheduler就能实现虚拟时间测试,无需等待真实时长。

示例代码(测试debounce运算符):

import AsyncAlgorithms
import CombineSchedulers
import XCTest

final class AsyncAlgorithmsTimeTests: XCTestCase {
    func testDebounceOperator() async {
        let testScheduler = TestScheduler()
        let inputStream = AsyncStream<String> { continuation in
            continuation.yield("initial")
            continuation.yield("final")
            continuation.finish()
        }
        
        var collectedOutput: [String] = []
        
        await testScheduler.run {
            // 将TestScheduler传入debounce的scheduler参数
            for await value in inputStream.debounce(for: .seconds(1), scheduler: testScheduler) {
                collectedOutput.append(value)
            }
        }
        
        // 未推进时间时,debounce还未触发输出
        XCTAssertTrue(collectedOutput.isEmpty)
        
        // 推进1秒虚拟时间
        testScheduler.advance(by: .seconds(1))
        
        // 此时debounce完成,仅收集到最后一次输入
        XCTAssertEqual(collectedOutput, ["final"])
    }
}

三、通用优化建议:避免真实等待的核心要点

  • 所有依赖时间的async函数/运算符,都要设计成可注入自定义调度器的形式,不要硬编码使用全局调度器(比如DispatchQueue.main或Task.sleep的默认调度器)。
  • 示例:将生产代码改为支持调度器注入
// 生产代码:支持传入自定义调度器,默认使用全局调度器
func loadRemoteData(with scheduler: some Scheduler = DispatchQueue.global()) async throws -> Data {
    try await scheduler.sleep(for: .seconds(3))
    return try Data(contentsOf: URL(string: "https://example.com/data")!)
}

// 测试代码:注入TestScheduler实现虚拟时间测试
func testLoadRemoteData() async throws {
    let testScheduler = TestScheduler()
    var loadedData: Data?
    
    await testScheduler.run {
        loadedData = try? await loadRemoteData(with: testScheduler)
    }
    
    XCTAssertNil(loadedData)
    testScheduler.advance(by: .seconds(3))
    XCTAssertNotNil(loadedData)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 18:45:33