简体   繁体   English

如何在 Swift 中存根 URLSession?

[英]How to stub URLSession in Swift?

I have been following this tutorial to stub out URLSession .我一直在关注本教程来删除URLSession The example was done by creating a protocol and extending the existing 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
    }
}

The unit tests work as expected.单元测试按预期工作。 But when I try to run the real thing, the URLSession -> datatask() gets into an infinite loop and it crashes.但是当我尝试运行真实的东西时, URLSession -> datatask() 进入无限循环并崩溃。 It seems to be that datatask() is calling itself.似乎 datatask() 正在调用自己。

What am I overlooking, please?请问我在看什么?

UPDATE:更新:

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

extension URLSessionDataTask: URLSessionDataTaskProtocol {}

I have finally found the solution.我终于找到了解决方案。 It's fascinating as we missed the wood for the trees.这很有趣,因为我们只见树木不见森林。 There are two issues:有两个问题:

1) It seems that Swift 4 has changed the signature for dataTask(with: NSURLRequest) to dataTask(with: URLRequest) 1) Swift 4 似乎已经将dataTask(with: NSURLRequest)的签名更改为dataTask(with: URLRequest)

Therefore the line in my opening question would only match to the Protocol's func signature, and it would never hit the dataTask inside URLSession , hence the infinite loop.因此,我的开头问题中的行只会与协议的 func 签名匹配,并且它永远不会命中dataTask URLSession ,因此是无限循环。 To solve this issue I had to change NSURLRequest to URLRequest and refactor the code accordingly.为了解决这个问题,我不得不将NSURLRequest更改为URLRequest并相应地重构代码。

2) The signature remains vague, hence it is better to store the result as dataTask first with a cast to URLSessionDataTask and then return the variable. 2) 签名仍然模糊,因此最好先将结果存储为URLSessionDataTask ,然后转换为URLSessionDataTask ,然后返回变量。

New refactored Code for Swift 4: 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
    }
}

I also found I had to inject URLSession.shared as a singleton and not as URLSession() , otherwise it could crash.我还发现我必须将URLSession.shared作为单例而不是URLSession()注入,否则它可能会崩溃。

Came here to understand how to mock URLSession tasks such as URLSessionDataTask ?来到这里是为了了解如何模拟URLSession任务,例如URLSessionDataTask

Circa Swift 5, it is much easier to mock the URLProtocol that URLSession is using to send the request.大约5斯威夫特,这是容易嘲笑URLProtocolURLSession使用发送请求。

See Unit Testing URLSession using URLProtocol.请参阅使用 URLProtocol 对 URLSession 进行单元测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM