如何在 Swift 中存根 URLSession?
How to stub URLSession in Swift?
我一直在关注这个 tutorial to stub out URLSession
. The example 是通过创建一个协议并扩展现有的 URLSession
来完成的。
protocol URLSessionProtocol {
typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void
func dataTask(with request: NSURLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol
}
extension URLSession: URLSessionProtocol {
func dataTask(with request: NSURLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol {
return dataTask(with: request, completionHandler: completionHandler) as URLSessionDataTaskProtocol
}
}
单元测试按预期工作。但是当我尝试 运行 真实的东西时, URLSession -> datatask() 进入无限循环并崩溃。似乎是 datatask() 正在调用自己。
请问我忽略了什么?
更新:
protocol URLSessionDataTaskProtocol {
var originalRequest: URLRequest? { get }
func resume()
}
extension URLSessionDataTask: URLSessionDataTaskProtocol {}
我终于找到了解决办法。这很迷人,因为我们只见树木不见森林。有两个问题:
1) Swift 4 似乎已将 dataTask(with: NSURLRequest)
的签名更改为 dataTask(with: URLRequest)
因此,我开篇问题中的那一行只会与协议的 func 签名相匹配,它永远不会碰到 URLSession
内的 dataTask
,因此会出现无限循环。为了解决这个问题,我不得不将 NSURLRequest
更改为 URLRequest
并相应地重构代码。
2) 签名仍然模糊,因此最好先将结果存储为 dataTask,然后转换为 URLSessionDataTask
,然后 return 变量。
Swift 4 的新重构代码:
typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void
protocol URLSessionProtocol {
func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol
}
extension URLSession: URLSessionProtocol {
func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol {
let task:URLSessionDataTask = dataTask(with: request, completionHandler: {
(data:Data?, response:URLResponse?, error:Error?) in completionHandler(data,response,error) }) as URLSessionDataTask
return task
}
}
我还发现我必须将 URLSession.shared
作为单例而不是 URLSession()
注入,否则它可能会崩溃。
来到这里是为了了解如何模拟 URLSession
任务,例如 URLSessionDataTask
?
大约 Swift 5,模拟 URLSession
用来发送请求的 URLProtocol
更容易 。
我一直在关注这个 tutorial to stub out URLSession
. The example 是通过创建一个协议并扩展现有的 URLSession
来完成的。
protocol URLSessionProtocol {
typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void
func dataTask(with request: NSURLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol
}
extension URLSession: URLSessionProtocol {
func dataTask(with request: NSURLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol {
return dataTask(with: request, completionHandler: completionHandler) as URLSessionDataTaskProtocol
}
}
单元测试按预期工作。但是当我尝试 运行 真实的东西时, URLSession -> datatask() 进入无限循环并崩溃。似乎是 datatask() 正在调用自己。
请问我忽略了什么?
更新:
protocol URLSessionDataTaskProtocol {
var originalRequest: URLRequest? { get }
func resume()
}
extension URLSessionDataTask: URLSessionDataTaskProtocol {}
我终于找到了解决办法。这很迷人,因为我们只见树木不见森林。有两个问题:
1) Swift 4 似乎已将 dataTask(with: NSURLRequest)
的签名更改为 dataTask(with: URLRequest)
因此,我开篇问题中的那一行只会与协议的 func 签名相匹配,它永远不会碰到 URLSession
内的 dataTask
,因此会出现无限循环。为了解决这个问题,我不得不将 NSURLRequest
更改为 URLRequest
并相应地重构代码。
2) 签名仍然模糊,因此最好先将结果存储为 dataTask,然后转换为 URLSessionDataTask
,然后 return 变量。
Swift 4 的新重构代码:
typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void
protocol URLSessionProtocol {
func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol
}
extension URLSession: URLSessionProtocol {
func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol {
let task:URLSessionDataTask = dataTask(with: request, completionHandler: {
(data:Data?, response:URLResponse?, error:Error?) in completionHandler(data,response,error) }) as URLSessionDataTask
return task
}
}
我还发现我必须将 URLSession.shared
作为单例而不是 URLSession()
注入,否则它可能会崩溃。
来到这里是为了了解如何模拟 URLSession
任务,例如 URLSessionDataTask
?
大约 Swift 5,模拟 URLSession
用来发送请求的 URLProtocol
更容易 。