Spyable 是一个强大的 Swift 工具,可以自动创建符合协议的类。最初设计用于通过生成 spy 来简化测试,现在已被广泛用于各种场景,例如 SwiftUI 预览或创建快速的虚拟实现。
Spyable 通过以下功能增强您的 Swift 工作流程
@Spyable
注释协议,并让宏生成相应的 spy 类。accessLevel
参数来覆盖继承的访问级别。import Spyable
@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
}
}
}
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
支持的值为
.public (公开)
.package (包访问)
.internal (内部)
.fileprivate (文件私有)
.private (私有)
使用 behindPreprocessorFlag
参数将生成的代码包装在预处理器指令中
@Spyable(behindPreprocessorFlag: "DEBUG")
protocol DebugProtocol {
func logSomething()
}
生成
#if DEBUG
internal class DebugProtocolSpy: DebugProtocol {
internal func logSomething() { ... }
}
#endif
将 Spyable 作为包依赖项添加
https://github.com/Matejkob/swift-spyable
添加到您的 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。