SuperAPI AI 工具集

swift-protocol-di-testing

测试与质量

查看项目

更新于 10-16 04:39

技能介绍

用途

本技能介绍一种基于协议的依赖注入(Protocol-based Dependency Injection)模式,目标是让涉及外部依赖的 Swift 代码变得可测试。它通过把文件系统、网络、iCloud 等外部依赖抽象到精简、功能集中的协议(Protocols)后面,使测试可以在不进行实际 I/O 的情况下确定性地运行。

何时激活

  • 编写访问文件系统、网络或外部 API 的 Swift 代码时
  • 需要在不触发实际故障的情况下测试错误处理路径时
  • 构建需要在不同环境(App、测试、SwiftUI 预览)下运行的模块时
  • 使用 Swift 并发(Concurrency,如 Actor、Sendable)设计可测试架构时

主要能力与核心模式

1. 定义精简且功能集中的协议

每个协议只处理一个特定的外部关注点。资料给出的示例包括:FileSystemProviding(容器 URL)、FileAccessorProviding(读写与存在性检查)、BookmarkStorageProviding(书签存取)。这些协议均声明为 Sendable。

2. 创建默认(生产环境)实现

例如 DefaultFileSystemProvider 使用 FileManager.default.url(forUbiquityContainerIdentifier:),DefaultFileAccessor 基于 Data(contentsOf:)、data.write(to:options:.atomic) 与 FileManager.default.fileExists(atPath:)。

3. 创建用于测试的模拟实现(Mock)

资料给出 MockFileAccessor:内部维护 files: [URL: Data] 字典,并提供 readError、writeError 可配置错误属性,用于模拟失败路径;标记为 final class ... @unchecked Sendable。另在测试示例中使用了 MockFileSystemProvider(containerURL:)。

4. 使用默认参数注入依赖

示例 SyncManager 为 actor,构造器参数 fileSystem 与 fileAccessor 使用生产环境默认实现,测试时注入 Mock。sync() 在拿不到容器 URL 时抛出 SyncError.containerNotAvailable,否则读取 data.json 并处理数据。

5. 使用 Swift 测试框架编写测试

示例使用 import Testing,配合 @Test、#expect、#expect(throws:) 断言:验证容器缺失时抛出 SyncError.containerNotAvailable、读取数据正确、读取错误时优雅处理等场景。

最佳实践

  • 单一职责:每个协议只处理一个关注点,不要创建拥有过多方法的“万能协议(God Protocols)”
  • 符合 Sendable:协议在 Actor 边界之间使用时为必须
  • 默认参数:生产代码默认使用真实实现,仅在测试中指定模拟对象
  • 错误模拟:为模拟对象设计可配置的错误属性,以便测试失败路径
  • 仅对边界进行模拟:模拟外部依赖(文件系统、网络、API),不要对内部类型进行模拟

需避免的反模式

  • 创建一个涵盖所有外部访问的大而全的协议
  • 对没有外部依赖的内部类型进行模拟
  • 使用 #if DEBUG 条件编译块而不是正确的依赖注入
  • 在与 Actor 配合使用时忘记让协议符合 Sendable
  • 过度工程:如果一个类型没有外部依赖,则不需要为其定义协议

适用场景

  • 任何涉及文件系统、网络或外部 API 的 Swift 代码
  • 测试在真实环境下难以触发的错误处理路径
  • 构建需要同时在 App、测试和 SwiftUI 预览上下文中运行的模块
  • 使用 Swift 并发(Actor、结构化并发)并需要可测试架构的应用

限制

资料未提供具体的安装命令、包管理方式或平台版本要求,暂未提供相关信息。