如何从 Swift 通用函数中捕获参数
How to capture arguments from Swift generic function
我有一个 Swift 协议,它定义了一个用于发出网络请求的通用函数。它看起来像这样:
protocol TaskManagerProtocol {
func run<V>(
task: Task<V>,
completion: @escaping (Result<V, Error>) -> Void
)
}
这在生产代码中非常有效,让我可以发出接收不同类型结果的请求。到目前为止,还不错。
问题:对于单元测试,我想要一个 Test Spy 来捕获参数,尤其是闭包。间谍看起来像这样:
class TaskManagerSpy<Value>: TaskManagerProtocol {
var callCount = 0
var task: [Task<Value>] = []
var completion: [(Result<Value, Error>) -> Void] = []
func run<V>(
task: Task<V>,
completion: @escaping (Result<V, Error>) -> Void
) {
guard V.self == Value.self else {
fatalError("run<V> doesn't match init<Value>")
}
callCount += 1
self.task.append(task)
self.completion.append(completion)
}
}
我的意图是让测试代码实例化 TaskManagerSpy<SomeType>
并将其注入到调用 run<V>(task:completion:)
的被测系统中。但就目前而言,append()
调用无法编译:
Cannot convert value of type 'Task<V>' to expected argument type 'Task<Value>'
闭包也一样。如果我注释掉这些行,测试就会成功而不会触发致命错误。
问题:我已经证明了类型是一样的。有没有办法将一种类型强制转换为另一种类型,以便我可以捕获参数?
因为正如你所说,你已经断言 V.self == Value.self
,你应该能够强制转换 task
:
self.task.append(task as! Task<Value>)
completion
也一样:
self.completion.append(completion as! ((Result<Value, Error>) -> Void))
您可以通过在 TaskManagerProtocol 中使用 associatedtype
而不是在 func run
上使用通用类型 V
来执行此操作:
protocol TaskManagerProtocol {
associatedtype ValueType
func run(
task: Task<ValueType>,
completion: @escaping (Result<ValueType, Error>) -> Void
)
}
class TaskManagerSpy<Value>: TaskManagerProtocol {
var callCount = 0
var task: [Task<Value>] = []
var completion: [(Result<Value, Error>) -> Void] = []
func run(
task: Task<Value>,
completion: @escaping (Result<Value, Error>) -> Void
) {
callCount += 1
self.task.append(task)
self.completion.append(completion)
}
}
我有一个 Swift 协议,它定义了一个用于发出网络请求的通用函数。它看起来像这样:
protocol TaskManagerProtocol {
func run<V>(
task: Task<V>,
completion: @escaping (Result<V, Error>) -> Void
)
}
这在生产代码中非常有效,让我可以发出接收不同类型结果的请求。到目前为止,还不错。
问题:对于单元测试,我想要一个 Test Spy 来捕获参数,尤其是闭包。间谍看起来像这样:
class TaskManagerSpy<Value>: TaskManagerProtocol {
var callCount = 0
var task: [Task<Value>] = []
var completion: [(Result<Value, Error>) -> Void] = []
func run<V>(
task: Task<V>,
completion: @escaping (Result<V, Error>) -> Void
) {
guard V.self == Value.self else {
fatalError("run<V> doesn't match init<Value>")
}
callCount += 1
self.task.append(task)
self.completion.append(completion)
}
}
我的意图是让测试代码实例化 TaskManagerSpy<SomeType>
并将其注入到调用 run<V>(task:completion:)
的被测系统中。但就目前而言,append()
调用无法编译:
Cannot convert value of type 'Task<V>' to expected argument type 'Task<Value>'
闭包也一样。如果我注释掉这些行,测试就会成功而不会触发致命错误。
问题:我已经证明了类型是一样的。有没有办法将一种类型强制转换为另一种类型,以便我可以捕获参数?
因为正如你所说,你已经断言 V.self == Value.self
,你应该能够强制转换 task
:
self.task.append(task as! Task<Value>)
completion
也一样:
self.completion.append(completion as! ((Result<Value, Error>) -> Void))
您可以通过在 TaskManagerProtocol 中使用 associatedtype
而不是在 func run
上使用通用类型 V
来执行此操作:
protocol TaskManagerProtocol {
associatedtype ValueType
func run(
task: Task<ValueType>,
completion: @escaping (Result<ValueType, Error>) -> Void
)
}
class TaskManagerSpy<Value>: TaskManagerProtocol {
var callCount = 0
var task: [Task<Value>] = []
var completion: [(Result<Value, Error>) -> Void] = []
func run(
task: Task<Value>,
completion: @escaping (Result<Value, Error>) -> Void
) {
callCount += 1
self.task.append(task)
self.completion.append(completion)
}
}