Unit Testing URLSession using URLProtocol.

Dhawal Dawar
4 min readJul 8, 2019

--

When it comes to writing unit test cases for the network layer that uses URLSession, I have seen most of the developers create their own protocol. An extension of URLSession implements that protocol, so during unit testing they could create a mock class by implementing that protocol and inject this dependency in the classes who perform network operations.

Here is what the above mentioned URLSession protocol looks like:

This is a decent approach but not at all required as Apple provides an elegant way to intercept network requests using URLProtocol. The other advantage of using this technique is you could use it in your existing project with minimum changes. All it needs is, injecting URLSession dependency in your classes which perform network operation so you could configure URLSession with your own MockURLProtocol during unit testing, we will see in a bit how it works.

Subclassing URLProtocol

URLProtocol

An abstract class that handles the loading of protocol-specific URL data.

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:

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:

API

To demonstrate the test cases, I have created a class named “PostDetailAPI” whose primary responsibility to fetch post detail by calling the network API, which gives a response in json format.

If you look at the initializer, it takes URLSession that has a default value of shared instance. This is the place where we will inject URLSession configured with MockURLProtocol for our test cases.

Unit Testing

Finally, we are all set to write test cases, first we will see how to configure URLSession with MockURLProtocol class that we have created above.

You can see we are providing an array of protocol classes to the configuration so when the request gets fired, system will create an instance of registered protocol classes and call “canInit” method on them to see if the class handles the current request. If yes, it will give responsibility to the class to complete the network operation.

Success test case

First we will test the success case, here we will send correct json response data and see if the API client gives us decoded “Post” object with correct values. To do so, we will follow below steps:

  1. Prepare mock json data.
  2. Assign request handler Closure, so whenever an API gets called MockURLProtocol will call the handler with a request and ask for the mock response.
  3. Validate received request, here we are only validating a URL but you could also validate other components of the request like headers, HTTP method etc.
  4. Create success HTTPURLResponse and return it along with the data.
  5. At last, call “fetchPostDetail” method and in the completion block, write all the assert cases to make sure we received correct “Post” object by validating all its data members.

Here is the code:

Failure test case

Now we will test the failure case, here we will send incorrect data and see if the API client gives us parsing error, here is the code:

Alamofire

While working with third party libraries sometimes it is difficult to mock implementation, but this is not the case with Alamofire. Just like URLSession, Alamofire internally uses its own SessionManager to manage the network calls and the good thing is, it is also configurable using URLSessionConfiguration. So you could provide your own protocolClasses to intercept the network calls just like we did above with URLSession. You just need to make sure you are injecting SessionManager dependency in your classes.

Below code demonstrates how to configure SessionManager:

Conclusion

So we have seen how easy is it to intercept network layer by creating our own URLProtocol without disturbing the existing code, the only requirement is you should inject URLSession dependency. You could also use the same technique to intercept Alamofire network calls.

I hope you liked this approach, please feel free to leave comments in case you have any questions or you feel you know a better way to intercept network layer.

--

--