模拟 URLSession 以返回模拟 URLSessionDataTask

Wis*_*uns 4 mocking swift

我想嘲笑URLSession,并返回一个嘲笑URLSessionDataTask

为了模拟URLSession我创建一个协议

protocol URLSessionProtocol {
    func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
}
Run Code Online (Sandbox Code Playgroud)

然后可以URLSession在扩展中符合

extension URLSession: URLSessionProtocol {}
Run Code Online (Sandbox Code Playgroud)

现在我想对 做同样的事情URLSessionDataTask,并为其实现类似的协议和扩展。我需要这样做,因为我调用的方式URLSession需要使用func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask

protocol URLSessionDataTaskProtocol {
    func resume()
}

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

那么我的URLSessionDataTask模拟设置如下:

class URLSessionMock: URLSessionProtocol {
    typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void
    // data and error can be set to provide data or an error
    var data: Data?
    var error: Error?
    func dataTask(
        with url: URL,
        completionHandler: @escaping CompletionHandler
        ) -> URLSessionDataTask {
        let data = self.data
        let error = self.error
        return URLSessionDataTaskMock {
            completionHandler(data, nil, error)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

URLSessionDataTaskMock提出:

class URLSessionDataTaskMock: URLSessionDataTaskProtocol {
    private let closure: () -> Void
    init(closure: @escaping () -> Void) {
        self.closure = closure
    }
    // override resume and call the closure

    func resume() {
        closure()
    }
}
Run Code Online (Sandbox Code Playgroud)

不起作用,因为URLSessionDataTaskMock内部URLSessionProtocol不是正确的返回类型 - 我需要返回一个URLSessionDataTask.

我无法将我的类型转换URLSessionDataTaskMockURLSessionDataTask,因为类型不相关。

我怎样才能URLSessionDataTaskMock从我的归还我的URLSessionProtocol

Rob*_*Rob 7

你也许可以摆脱这样的事情。关键是 URLSessionProtocol 中的关联类型

protocol URLSessionProtocol {
    associatedtype DataTaskType
    func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> DataTaskType
}

extension URLSession: URLSessionProtocol {}

protocol URLSessionDataTaskProtocol {
    func resume()
}

extension URLSessionDataTask: URLSessionDataTaskProtocol {}

class URLSessionDataTaskMock: URLSessionDataTaskProtocol {
    typealias CompletionHandler = URLSessionMock.CompletionHandler

    private let completion: CompletionHandler

    init(completion: @escaping CompletionHandler) {
        self.completion = completion
    }

    func resume() {
        // create some data
        completion(nil, nil, nil)
    }
}

class URLSessionMock: URLSessionProtocol {
    typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void
    // data and error can be set to provide data or an error
    var data: Data?
    var error: Error?
    func dataTask(
        with url: URL,
        completionHandler: @escaping CompletionHandler
    ) -> URLSessionDataTaskMock {
        return URLSessionDataTaskMock(completion: completionHandler)
    }
}
Run Code Online (Sandbox Code Playgroud)