Mostly we have a class like this to manage the network calls, nothing wrong in it, it will serve the purpose in elegant way.
class NetworkClient { enum Result { case data(Data) case error(Error) } func load(from url: URL, completionHandler: @escaping (Result) -> Void) { let task = URLSession.shared.dataTask(with: url) { (data, response, error) in if let error = error { return completionHandler(.error(error)) } completionHandler(.data(data ?? Data())) } task.resume() } }
but, writing test case for this kind of system provided singleton is not that much easier job.
we need to follow certain steps to make sure our things is working as we are expecting
These are the steps
1. Abstract into a protocol
2. Use the protocol with the singleton as the default
3. Mock the protocol in your tests
protocol NetworkEngine {
typealias Handler = (Data?, URLResponse?, Error?) -> Void
func performRequest(for url: URL, completionHandler: @escaping Handler)
}
extension URLSession: NetworkEngine {
typealias Handler = NetworkEngine.Handler
func performRequest(for url: URL, completionHandler: @escaping Handler) {
let task = dataTask(with: url, completionHandler: completionHandler)
task.resume()
}
}
class NetworkClient {
.
.
.
private let engine: NetworkEngine
init(engine: NetworkEngine = URLSession.shared) {
self.engine = engine
}
func load(from url: URL, completionHandler: @escaping (Result) -> Void) {
engine.performRequest(for: url) { (data, response, error) in
if let error = error {
return completionHandler(.error(error))
}
completionHandler(.data(data ?? Data()))
}
}
}
func testLoadingData() {
class NetworkEngineMock: NetworkEngine {
typealias Handler = NetworkEngine.Handler
var requestedURL: URL?
func performRequest(for url: URL, completionHandler: @escaping Handler) {
requestedURL = url
let data = “Hello world”.data(using: .utf8)
completionHandler(data, nil, nil)
}
}
let engine = NetworkEngineMock()
let loader = DataLoader(engine: engine)
var result: DataLoader.Result?
let url = URL(string: “my/API”)!
loader.load(from: url) { result = $0 }
XCTAssertEqual(engine.requestedURL, url)
XCTAssertEqual(result, .data(“Hello world”.data(using: .utf8)!))
}