swift-concurrency-extras

swift-concurrency-extras

增强Swift并发代码的可测试性与可靠性工具库

swift-concurrency-extras是一个开源库,为Swift并发编程提供实用工具集。该库增强了Swift并发代码的可测试性和可靠性,包含LockIsolated类型用于安全访问共享状态、Stream和Task相关辅助函数,以及串行执行工具。它主要面向需要编写可靠并发代码和单元测试的开发者,有助于简化异步编程中常见挑战的处理过程。

Swift并发编程测试异步编程开源库Github开源项目

swift-concurrency-extras

CI Slack

可靠且可测试的Swift并发。

了解更多

这个库旨在支持为Point-Free制作的库和剧集,Point-Free是一个由Brandon WilliamsStephen Celis主持的探索Swift编程语言的视频系列。

你可以在这里观看所有剧集。

<a href="https://www.pointfree.co/collections/concurrency/"> <img alt="视频海报图片" src="https://yellow-cdn.veclightyear.com/2b54e442/888106b8-0f78-4ea2-89fe-b7e23f5ed500.jpeg" width="600"> </a>

动机

这个库提供了许多工具,使Swift并发变得更容易使用和更易于测试。

LockIsolated

LockIsolated类型帮助将其他值包装在隔离的上下文中。它使用锁将值包装在一个类中,允许你通过同步接口读写该值。

该库为两种Swift流类型提供了多个辅助API:

  • 有一些辅助函数可以将任何AsyncSequence符合性擦除为任一具体流类型。这允许你将流类型视为某种"类型擦除"的AsyncSequence

    例如,假设你有这样一个依赖客户端:

    struct ScreenshotsClient { var screenshots: () -> AsyncStream<Void> }

    然后你可以构建一个将NotificationCenter.Notifications异步序列"擦除"为流的活跃实现:

    extension ScreenshotsClient { static let live = Self( screenshots: { NotificationCenter.default .notifications(named: UIApplication.userDidTakeScreenshotNotification) .map { _ in } .eraseToStream() // ⬅️ } ) }

    使用eraseToThrowingStream()来传播来自抛出异常的异步序列的失败。

  • Swift 5.9的makeStream(of:)函数已经被向后移植。它在需要覆盖返回流的依赖端点的测试中很有用:

    let screenshots = AsyncStream.makeStream(of: Void.self) let model = FeatureModel(screenshots: { screenshots.stream }) XCTAssertEqual(model.screenshotCount, 0) screenshots.continuation.yield() // 模拟截图 XCTAssertEqual(model.screenshotCount, 1)
  • 提供了静态的AsyncStream.neverAsyncThrowingStream.never辅助函数,它们表示永远存在且从不发出的流。它们在需要用永远挂起且不发出的流覆盖依赖端点的测试中很有用。

    let model = FeatureModel(screenshots: { .never })
  • 提供了静态的AsyncStream.finishedAsyncThrowingStream.finished(throwing:)辅助函数,它们表示立即完成且不发出的流。它们在需要用立即完成/失败的流覆盖依赖端点的测试中很有用。

任务

该库为Task类型增加了新功能。

  • 静态函数Task.never()可以异步返回任何类型的值,但通过永久挂起来实现。这对于以不需要实际从该端点返回数据的方式满足依赖要求很有用。

    例如,假设你有这样一个依赖客户端:

    struct SettingsClient { var fetchSettings: () async throws -> Settings }

    你可以在测试中通过等待Task.never()来覆盖客户端的fetchSettings端点,使其永久挂起:

    SettingsClient( fetchSettings: { try await Task.never() } )
  • Task.cancellableValue是一个属性,它等待非结构化任务的value属性,同时从当前异步上下文传播取消。

  • Task.megaYield()是一个粗暴的工具,可以通过多次挂起当前任务来使不稳定的异步测试变得稳定一些,增加其他异步工作有足够时间启动的机会。在可能的情况下,优先考虑使用串行执行的可靠性。

串行执行

由于运行时处理挂起点的方式,Swift中的一些异步代码notoriously难以测试。该库提供了一个静态函数withMainSerialExecutor,它试图串行和确定性地运行操作中产生的所有任务。这个函数可以用来使异步测试更快、更稳定。

警告:这个API仅用于测试,以使测试更可靠。请不要在应用程序代码中使用它。

我们说它"_尝试_串行和确定性地运行操作中产生的所有任务",因为它在底层依赖Swift运行时中的一个全局可变变量来完成工作,如果这个可变变量在操作期间发生变化,就无法保证其作用域。

例如,考虑以下看似简单的模型,它发起网络请求并在请求进行时管理isLoading状态:

@Observable class NumberFactModel { var fact: String? var isLoading = false var number = 0 // 显式注入请求依赖以使其可测试,也可以通过依赖管理库提供 let getFact: (Int) async throws -> String func getFactButtonTapped() async { self.isLoading = true defer { self.isLoading = false } do { self.fact = try await self.getFact(self.number) } catch { // TODO: 处理错误 } } }

我们希望能够编写一个测试来确认isLoading状态先变为true然后变为false。你可能希望它像这样简单:

func testIsLoading() async { let model = NumberFactModel(getFact: { "\($0) is a good number." }) let task = Task { await model.getFactButtonTapped() } XCTAssertEqual(model.isLoading, true) XCTAssertEqual(model.fact, nil) await task.value XCTAssertEqual(model.isLoading, false) XCTAssertEqual(model.fact, "0 is a good number.") }

然而,这几乎100%会失败。问题在于,创建非结构化Task后的下一行会在非结构化任务内部的行之前执行,所以我们永远不会检测到isLoading状态变为true的时刻。

你可能希望通过使用Task.yield来在getFactButtonTapped方法被调用和请求完成之间插入一些时间:

func testIsLoading() async { let model = NumberFactModel(getFact: { "\($0) is a good number." }) let task = Task { await model.getFactButtonTapped() } + await Task.yield() XCTAssertEqual(model.isLoading, true) XCTAssertEqual(model.fact, nil) await task.value XCTAssertEqual(model.isLoading, false) XCTAssertEqual(model.fact, "0 is a good number.") }

但这仍然在绝大多数情况下失败。

这些问题以及更多问题可以通过在主串行执行器上运行整个测试来解决。你还需要在getFact端点中插入一个小的yield,因为Swift能够内联不实际执行异步工作的异步闭包:

func testIsLoading() async { + await withMainSerialExecutor { let model = NumberFactModel(getFact: { + await Task.yield() return "\($0) is a good number." }) let task = Task { await model.getFactButtonTapped() } await Task.yield() XCTAssertEqual(model.isLoading, true) XCTAssertEqual(model.fact, nil) await task.value XCTAssertEqual(model.isLoading, false) XCTAssertEqual(model.fact, "0 is a good number.") + } }

这个小小的改变使得这个测试100%确定性地通过。

文档

该库的最新文档可在此处获取。

致谢

感谢Pat Brown和Thomas Grapperon在发布前对该库提供反馈。特别感谢Kabir Oberai帮助我们解决了Xcode的一个bug,并使我们能够在库中提供串行执行工具。

其他库

Concurrency Extras只是让Swift中编写可测试代码变得更容易的众多库之一。

  • Case Paths:用于处理和测试枚举的工具。

  • Clocks:一些时钟,使Swift并发更易于测试和更加多功能。

  • Combine Schedulers:一些调度器,使Combine更易于测试和更加多功能。

  • Composable Architecture:一个库,用于以一致且可理解的方式构建应用程序,考虑到了组合、测试和人体工程学。

  • Custom Dump:用于调试、比较和测试应用程序数据结构的工具集合。

  • Dependencies:受SwiftUI的"环境"启发的依赖管理库。

  • Snapshot Testing:通过记录和对比制品来断言你的应用程序。

  • XCTest Dynamic Overlay:从应用程序代码调用XCTFail和其他通常仅用于测试的辅助函数。

许可证

该库根据MIT许可证发布。详情请参见LICENSE

编辑推荐精选

讯飞智文

讯飞智文

一键生成PPT和Word,让学习生活更轻松

讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。

AI办公办公工具AI工具讯飞智文AI在线生成PPTAI撰写助手多语种文档生成AI自动配图热门
讯飞星火

讯飞星火

深度推理能力全新升级,全面对标OpenAI o1

科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。

热门AI开发模型训练AI工具讯飞星火大模型智能问答内容创作多语种支持智慧生活
Spark-TTS

Spark-TTS

一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型

Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。

Trae

Trae

字节跳动发布的AI编程神器IDE

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

AI工具TraeAI IDE协作生产力转型热门
咔片PPT

咔片PPT

AI助力,做PPT更简单!

咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制作出高品质 PPT,满足不同场景演示需求。

讯飞绘文

讯飞绘文

选题、配图、成文,一站式创作,让内容运营更高效

讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。

热门AI辅助写作AI工具讯飞绘文内容运营AI创作个性化文章多平台分发AI助手
材料星

材料星

专业的AI公文写作平台,公文写作神器

AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。

openai-agents-python

openai-agents-python

OpenAI Agents SDK,助力开发者便捷使用 OpenAI 相关功能。

openai-agents-python 是 OpenAI 推出的一款强大 Python SDK,它为开发者提供了与 OpenAI 模型交互的高效工具,支持工具调用、结果处理、追踪等功能,涵盖多种应用场景,如研究助手、财务研究等,能显著提升开发效率,让开发者更轻松地利用 OpenAI 的技术优势。

Hunyuan3D-2

Hunyuan3D-2

高分辨率纹理 3D 资产生成

Hunyuan3D-2 是腾讯开发的用于 3D 资产生成的强大工具,支持从文本描述、单张图片或多视角图片生成 3D 模型,具备快速形状生成能力,可生成带纹理的高质量 3D 模型,适用于多个领域,为 3D 创作提供了高效解决方案。

3FS

3FS

一个具备存储、管理和客户端操作等多种功能的分布式文件系统相关项目。

3FS 是一个功能强大的分布式文件系统项目,涵盖了存储引擎、元数据管理、客户端工具等多个模块。它支持多种文件操作,如创建文件和目录、设置布局等,同时具备高效的事件循环、节点选择和协程池管理等特性。适用于需要大规模数据存储和管理的场景,能够提高系统的性能和可靠性,是分布式存储领域的优质解决方案。

下拉加载更多