Our iOS Test Adventure: Mocking Requests
As mentioned in the first article of the serie, we started our test adventure by first mocking the requests.
After a little research we found a few libraries that will work for us. But there was a rule that we don’t use third party library if we can do what the library does.
MockHelper
Firstly, we created a singleton helper to store our JSON files.
We filled our json files according to their url. Helper search for JSON file from Bundle and store as dictionary ‘[String: Data]’ and returns Data whenever that url calls.
URLProtocol
URLProtocol lets you redefine how Apple’s URL Loading System operates, by defining custom URL schemes and redefining the behavior of existing URL schemes.
For every request, URL Loading system of Apple, searches for a registered protocol handler to be able to handle request to load a URL. All this protocols need have a method which name is canInitWithRequest to tell the system whether it can handle the request. If the answer is yes, the loading system will handle the request with this URLProtocol and ignore other registered protocols if exist.
There are several class functions that has to override.
canonicalRequest -> A protocol should guarantee that the same input request always yields the same canonical form. The meaning of canonical is up to protocol.
stopLoading -> A protocol should override this function but no need to do something in it. This method run just after the URL load.
startLoading -> If this method called, protocol should start loading the request. For our case, this is the most important method.
Since we want to mock all of our network calls, all of requests must be handled by our new URLProtocol class. To ensure that, canInitWithRequest function must return true.
We don’t care about what is the canonical version of request and what will happen just after the loading. So we can simply implement canonicalRequest and stopLoading as below.
We create response model that we expect at the end of the request.
startLoading function is most important function that has to be override. For our case, we want to send desired Data for that request to caller.
MockURLProtocol is ready to use. We have to register this protocol to URLProtocol before send a request.
At the end of the day, we can send a request.
Here is the output.
Conclusion
Unit tests has to be fast and repeatable.
Fast -> You should not wait remote service for a while.
Repeatable -> You should obtain the same results every time you run a test. External data providers could cause intermittent failures.
To ensure that our test cases are fast and repeatable, inputs must be same all the time. To do that we mock network layer and return same data for every test runs.
Thanks to Apple’s URL Loading System, we can create our own URLProtocol and make it behave the way we want.
Resources