I am writing some code to be able to make testing easier. After researching, I found a good way of making URLSession
testable is to make it conform to a protocol and use that protocol in the class I need to test. I applied the same method to URL
. However, I now need to downcast the url parameter as a URL type. This seems a bit "dangerous" to me. What is the proper way to make URL
testable? How can I also mock the URLSessionDataTask
type returned by dataTask
?
import Foundation
protocol URLSessionForApiRequest {
func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
}
protocol URLForApiRequest {
init?(string: String)
}
extension URLSession: URLSessionForApiRequest {}
extension URL: URLForApiRequest {}
class ApiRequest {
class func makeRequest(url: URLForApiRequest, urlSession: URLSessionForApiRequest) {
guard let url = url as? URL else { return }
let task = urlSession.dataTask(with: url) { _, _, _ in print("DONE") }
task.resume()
}
}
You want to make a protocol that wraps the function that you are going to call, then make a concrete implementation and mock implementation that returns whatever you initialize it with. Here is an example:
import UIKit
import PlaygroundSupport
protocol RequestProvider {
func request(from: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void)
}
class ApiRequest: RequestProvider {
func request(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume
}
}
class MockApiRequest: RequestProvider {
enum Response {
case value(Data, URLResponse), error(Error)
}
private let response: Response
init(response: Response) {
self.response = response
}
func request(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void) {
switch response {
case .value(let data, let response):
completion(data, response, nil)
case .error(let error):
completion(nil, nil, error)
}
}
}
class SomeClassThatMakesAnAPIRequest {
private let requestProvider: RequestProvider
init(requestProvider: RequestProvider) {
self.requestProvider = requestProvider
}
//Use the requestProvider here and it uses either the Mock or the "real" API provider based don what you injected
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.