Spyable

GitHub Workflow Status codecov

Spyable 是一个强大的 Swift 工具,可以自动创建符合协议的类。最初设计用于通过生成 spy 来简化测试,现在已被广泛用于各种场景,例如 SwiftUI 预览或创建快速的虚拟实现。

概述

Spyable 通过以下功能增强您的 Swift 工作流程

快速入门

  1. 导入 Spyable:import Spyable
  2. 使用 @Spyable 注释您的协议
@Spyable
public protocol ServiceProtocol {
  var name: String { get }
  func fetchConfig(arg: UInt8) async throws -> [String: String]
}

这将生成一个名为 ServiceProtocolSpy 的 spy 类,具有 public 访问级别。 生成的类包含用于跟踪方法调用、参数和返回值的属性和方法。

public class ServiceProtocolSpy: ServiceProtocol {
  public var name: String {
    get { underlyingName }
    set { underlyingName = newValue }
  }
  public var underlyingName: (String)!

  public var fetchConfigArgCallsCount = 0
  public var fetchConfigArgCalled: Bool {
    return fetchConfigArgCallsCount > 0
  }
  public var fetchConfigArgReceivedArg: UInt8?
  public var fetchConfigArgReceivedInvocations: [UInt8] = []
  public var fetchConfigArgThrowableError: (any Error)?
  public var fetchConfigArgReturnValue: [String: String]!
  public var fetchConfigArgClosure: ((UInt8) async throws -> [String: String])?

  public func fetchConfig(arg: UInt8) async throws -> [String: String] {
    fetchConfigArgCallsCount += 1
    fetchConfigArgReceivedArg = (arg)
    fetchConfigArgReceivedInvocations.append((arg))
    if let fetchConfigArgThrowableError {
      throw fetchConfigArgThrowableError
    }
    if fetchConfigArgClosure != nil {
      return try await fetchConfigArgClosure!(arg)
    } else {
      return fetchConfigArgReturnValue
    }
  }
}
  1. 在您的测试中使用 spy
func testFetchConfig() async throws {
  let serviceSpy = ServiceProtocolSpy()
  let sut = ViewModel(service: serviceSpy)

  serviceSpy.fetchConfigArgReturnValue = ["key": "value"]

  try await sut.fetchConfig()

  XCTAssertEqual(serviceSpy.fetchConfigArgCallsCount, 1)
  XCTAssertEqual(serviceSpy.fetchConfigArgReceivedInvocations, [1])

  try await sut.saveConfig()

  XCTAssertEqual(serviceSpy.fetchConfigArgCallsCount, 2)
  XCTAssertEqual(serviceSpy.fetchConfigArgReceivedInvocations, [1, 1])
}

高级用法

访问级别继承和覆盖

默认情况下,生成的 spy 会继承注释协议的访问级别。 例如

@Spyable
internal protocol InternalProtocol {
  func doSomething()
}

这将生成

internal class InternalProtocolSpy: InternalProtocol {
  internal func doSomething() { ... }
}

您可以通过显式指定访问级别来覆盖此行为

@Spyable(accessLevel: .fileprivate)
public protocol CustomProtocol {
  func restrictedTask()
}

生成

fileprivate class CustomProtocolSpy: CustomProtocol {
  fileprivate func restrictedTask() { ... }
}

accessLevel 支持的值为

限制 Spy 可用性

使用 behindPreprocessorFlag 参数将生成的代码包装在预处理器指令中

@Spyable(behindPreprocessorFlag: "DEBUG")
protocol DebugProtocol {
  func logSomething()
}

生成

#if DEBUG
internal class DebugProtocolSpy: DebugProtocol {
  internal func logSomething() { ... }
}
#endif

安装

Xcode 项目

将 Spyable 作为包依赖项添加

https://github.com/Matejkob/swift-spyable

Swift 包管理器

添加到您的 Package.swift

dependencies: [
  .package(url: "https://github.com/Matejkob/swift-spyable", from: "0.3.0")
]

然后,将 product 添加到您的 target

.product(name: "Spyable", package: "swift-spyable"),

许可证

此库基于 MIT 许可证发布。 有关详细信息,请参阅 LICENSE