Mock network layer for unit testing in Swift
Mock network layer for unit testing is an interesting topic. It will help you to make your unit tests more robust. Many times the models, wrappers and modules that you write depend on a network requests. For different responses you’ll need different behaviour. How will the class recover from a failed request? Network requests are asynchronous, how can we handle that? One of the rules in unit testing is that unit tests does not make network requests. How does that fit into testing the network layer?
 
The answer to many of these questions is mocking. By implementing mock object, we can predefine the network responses to fit the test cases. Then by using dependency injection, we can set the mocked network layer to replace the real one. Good news is that this not all that difficult. Stick with me for few minutes and I’ll show you how to do it. I also wrote an article about unit testing viewModels, where I also cover this technique. Since there is a lot information in it, I wanted to separate this piece in to it’s own post.

AppServerClient which is used to mock network layer

Mock network layer

Let’s say we have an interface for downloading data from server. Let’s call it AppServerClient. AppServerClient has a method called getFriends. It returns a list of friends if network request is successful and there is some data stored in the web service. So we need to mock the AppServerClient for testing purposes. We can do this by subclassing the original service:

MockAppServerClient is a sub class of AppServerClient. What we need to do here is to override the function that we need in our unit tests. There is also one class variable that AppServerClient doesn’t have getFriendResult. We will use this to specify the result the test expects. The result is a parameter in the completion block. Even though it is against most of the style guides to use !-mark, it is ok to use it here. In testing if the force unwrap fails, it means there is something wrong in code that sets up the test case. Also using the !-mark in testing target, won’t crash the app in production.
 
We place this private class inside file that we are testing the module that is using it. Since it we define it as private, it won’t interfere with the rest of the unit test classes.
 
Now we have the mock network layer ready. Next, let’s see how we can use it inside the module we are testing. This case inside a ViewModel.

Mock network layer success case

AppServerClient uses Result to communicate the response to who ever is calling it. Result is an enum that contains 2 cases: success(payload:T) & [rad-hl]failure(U?).

If you are not familiar with Result, it either returns a success or failure. Success contains a payload received with the request, this case an array of Friends. Failure case returns the error so that the caller can handle it.
 
Now it is time to write the test for a network request that completes successfully:

We created a test called testSuccess. First, we need to create an instance of the MockAppServerClient. Then we’ll set the getFriendResult to fit the expected result of our test case. For the sake of simplicity the getFriendResult now returns an empty array. Next, we’ll create an instance of the view model and pass AppServerClient as a parameter.

Here, we use dependency injection to make sure ViewModel is using the mock instance of AppServerClient. When testing against the mock network layer, we need to know how the module using it actually works. After receiving the friends, ViewModel tries to create tableViewCells from the response. Since the getFriendResult is set to an empty array, we know there is no cells available. We can check that by using the guard case statement and make sure the binded cell type is empty. If that is not the case XCTFail() marks the test as a failure.

Now, let’s test for a failing case

Mock network layer failing case

We map network request failures to human readable format in AppServerClient.

So this is what we will set for the response in the case of a failing request. Here is the complete failure test case called testFailed:

Again, let’s create an instance of MockAppServerClient. This time the result is set to failure[rad-hl]. The value inside the failure case is an error of type notFound. We defined this in the  [rad-hl]GetFriendFailureReason  above. Next, we will do the same things as in the successful case. Only difference here is, this time the cells created contains an error cell. If that is not the case XCTFail() will again mark the test as a failure.

Conclusion

Mocking network layer  is a crucial technique to set up your safety net by unit testing. It is not all that hard, but for a long time I had an image in my head of it being difficult. Since you and I now know that is not the case, I hope that you’ll also start using it. I encourage you to check out the more thorough article about unit testing viewmodel in Swift. That article goes a bit deeper into the world of unit testing.

As usual if you have any question, leave a comment, email or ping me on twitter! Thank you for reading and have a great day my friend!