如何在Swift中存根URLSession?

Hou*_*man 5 unit-testing ios swift urlsession swift4

我一直在按照本教程进行讲解URLSession。通过创建协议并扩展现有协议来完成该示例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
    }
}
Run Code Online (Sandbox Code Playgroud)

单元测试按预期工作。但是,当我尝试运行真实对象时,URLSession-> datatask()陷入无限循环并崩溃。似乎datatask()正在调用自身。

请问我在俯视什么?

更新:

protocol URLSessionDataTaskProtocol {
    var originalRequest: URLRequest? { get }
    func resume()
}

extension URLSessionDataTask: URLSessionDataTaskProtocol {}
Run Code Online (Sandbox Code Playgroud)

Hou*_*man 3

我终于找到了解决方案。它\xe2\x80\x99很有趣,因为我们只见树木不见森林。有两个问题:

\n\n

1) Swift 4 似乎已将签名更改dataTask(with: NSURLRequest)dataTask(with: URLRequest)

\n\n

因此,我的开头问题中的行只会与协议的 func 签名匹配,并且它永远不会到达内部dataTaskURLSession因此会出现无限循环。为了解决这个问题,我必须相应地更改NSURLRequestURLRequest重构代码。

\n\n

2)签名仍然模糊,因此最好先将结果存储为 dataTask 并进行强制转换URLSessionDataTask,然后返回变量。

\n\n

Swift 4 的新重构代码:

\n\n
typealias DataTaskResult = (Data?, URLResponse?, Error?) -> Void\n\nprotocol URLSessionProtocol {\n    func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol\n}\n\nextension URLSession: URLSessionProtocol {\n    func dataTask(with request: URLRequest, completionHandler: @escaping DataTaskResult) -> URLSessionDataTaskProtocol {\n        let task:URLSessionDataTask = dataTask(with: request, completionHandler: {\n            (data:Data?, response:URLResponse?, error:Error?) in completionHandler(data,response,error) }) as URLSessionDataTask\n        return task\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我还发现我必须URLSession.shared作为单例注入,而不是作为 注入URLSession(),否则它可能会崩溃。

\n