# TDD; Implementing Network Service Class

When we initiate a request, internally system checks for registered protocol classes which can handle the request and when it finds one it gives the responsibility to that class to complete the network operation. Through this way, we could intercept the network layer, all we have to do is create our mock class by extending URLProtocol and override all the required methods that will help us to write unit test cases, just like below:

class MockURLProtocol: URLProtocol {
  
  override class func canInit(with request: URLRequest) -> Bool {
    // To check if this protocol can handle the given request.
    return true
  }

  override class func canonicalRequest(for request: URLRequest) -> URLRequest {
    // Here you return the canonical version of the request but most of the time you pass the orignal one.
    return request
  }

  override func startLoading() {
    // This is where you create the mock response as per your test case and send it to the URLProtocolClient.
  }

  override func stopLoading() {
    // This is called if the request gets canceled or completed.
  }
}

Now we will modify above class with the actual logic that will help our test cases to validate a request and generate a mock response to be sent to the client of the API. We will follow below step for the same:

  1. Declare static variable requestHandler of closure type that will be handled by the test case to validate a request and generate mock response.
  2. Call requestHandler closure with the received request and capture the tuple of mocked response and data generated by the test case.
  3. Call didReceive delegate method to notify client with the response.
  4. Call didLoad delegate method to notify client with the data.
  5. Finally, call didFinishLoading delegate method to notify the client that the request has been completed successfully.
  6. If the test case throws an error, handle it and call the didFailWithError delegate method to notify the client that something went wrong. Here is the code:
// 1. Handler to test the request and return mock response.
static var requestHandler: ((URLRequest) throws -> (HTTPURLResponse, Data?))?
  
override func startLoading() {
  guard let handler = MockURLProtocol.requestHandler else {
    fatalError("Handler is unavailable.")
  }
    
  do {
    // 2. Call handler with received request and capture the tuple of response and data.
    let (response, data) = try handler(request)
    
    // 3. Send received response to the client.
    client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
    
    if let data = data {
      // 4. Send received data to the client.
      client?.urlProtocol(self, didLoad: data)
    }
    
    // 5. Notify request has been finished.
    client?.urlProtocolDidFinishLoading(self)
  } catch {
    // 6. Notify received error.
    client?.urlProtocol(self, didFailWithError: error)
  }
}

Altenative Way (opens new window)

# References

Unit Testing URLSession using URLProtocol (opens new window)

Custom Mock Network Request (opens new window)