Mockable Networking layer in iOS

Dan Marcenco
4 min readFeb 8, 2022

--

The Problem

One of the core parts of every mobile application is the networking layer. The way it is done depends on the reliability, scalability and subsequent support of the application. Last time I was involved in FinTech projects. When you work with finances, the question of security and code quality always arises. The quality of the code is above all else, and tests are the best tool to achieve it.

In this article, I would like to share with you one way of how you can create a scalable and testable networking layer in Swift.

A technique such as mocking comes to our rescue. Mocking is a standard practice used to test requests without punching the real API. Mock – in OOP is an object that serves for testing purposes and behaves just like a real object, but is not “real”. Here are some of the reasons why it is bad practice to use real queries in testing:

  • Unit Tests are supposed to be fast and reliable. Actually communicating with the server won’t only make the unit tests extremely slow, but also unreliable due to dependence on the internet connection.
  • Actually communicating with the server would disrupt the backend. Example: Let’s say you are unit testing networking code to delete a user. Making an actual call to the server would lead to actual deletion of the user, we don’t want that.

What to do?

We have to replace the URLSession class with a completely simulated implementation of it. We will also teach our networking client to parse the received request into the Decodable model.

NOTE: In this article we will write our networking layer using standard closure-based implementation. In the next article we will try to refactor our mockable networking layer with the help of async/await, cause async/await is only available starting from iOS 15.0.

  • First of all, we need to abstract the required API from the URLSession. To do this, we will write the NetworkSession protocol:
  • Then you need to make the URLSession to implement this protocol:

And now we can start writing our mockable NetworkManager client:

In the implementation of the NetworkManager, we can notice that this class accepts an object that follows the NetworkSession protocol, but not a real URLSession. This will help us inject a mocked class here for tests. Inside the initializer, we declare the default URLSession.shared.

Pay attention at the makeRequest method. This method was made in such a way that it is possible to parse a generic Decodable model. As a decodable parameter, we need to pass the Decodable object there, which we will eventually parse. As a completion block, we see a Result enum, which will return our expected model if it is successful or return an Error if something goes wrong with decoding.

Practice

Let’s see how our NetworkManager can be used with a real example. Let’s use a wonderful API - https://randomuser.me

Create the Decodable model - User, according to the documentation – https://randomuser.me/documentation#results

Here is the example of the data loader which uses the NetworkManager:

Now let’s move to the most important point of this article — testing our Networking Layer.

At the beginning of the article, we discussed that we will test requests without knocking on a real server. What do we need for this?

  • Create a fixture of User model in a file users_fixture.json:
  • Write a helper which will convert our fixture to Data for making requests. In the future we will use DataFixtures helper for managing test fixtures.
  • Create a mock class which will implement the NetworkSession protocol.

The NetworkSessionMock acts like a URLSession. Pay attention here at the requestUrl. We’ve created this variable to have a possibility to check that we use correct url to make a HTTP request. Also we could substitute the expected results using the data, or to write a fail test case by substituting the error.

And finally, we will write a test for our UserListLoader, which calls the NetworkManager under the hood.

Final words

Let’s recap what we’ve done here.

  1. We injected our User fixture to NetworkSessionMock()
  2. We initialized our mockable NetworkManager where we could easily inject our mocked network session
  3. We called loadData using our fake networkManager
  4. We checked that we use correct url for making the HTTP request, and that we recieve the User results

Thanks to this test we could easily simulate the network requests and check that our Decodable model is built correctly.

There actually are many different ways to test networking. In this article, we walked you through one of the easiest ways to use a native URLSession.

For better understanding I’ve created a sample project:

Thanks for reading!

--

--