avatarSteven Curtis

Summary

The article outlines a method for creating and testing a networking manager using Alamofire in Swift, without making real network calls.

Abstract

The author, Steven Curtis, presents a comprehensive guide to building a fully testable networking manager by leveraging Alamofire in Swift. The article emphasizes the importance of testing network-related classes without the need for actual network requests. It covers the creation of an APIRouter protocol, the use of dependency injection, and the implementation of a URLProtocolMock for simulating network responses during tests. The author provides examples and gists to illustrate the process, including the setup of Alamofire as a dependency, the construction of a router for URL building and header application, and the integration of a network manager protocol. The article also touches on the overall strategy for testing, including the use of a mock router and the injection of a mock session into the view controller for testing purposes. The conclusion reiterates the significance of avoiding real API calls during testing and invites readers to explore the accompanying code repository and engage with the author on Twitter for further discussion.

Opinions

  • The author believes that testing network managers is crucial and achievable without real network calls.
  • Steven Curtis advocates for the use of Alamofire's router for URL string compilation and header application, finding it equivalent to his own method of building URLs in Swift.
  • The author suggests that overriding the Session to inject a URLSessionMock is not the ideal strategy in Alamofire's design philosophy.
  • The article implies that using a URLProtocolMock is a superior approach for testing network operations, as it allows for simulating network responses without relying on real network calls.
  • The author emphasizes that the practices outlined in the article are not intended for production code, particularly accessing the NetworkManager directly from a view controller.
  • Steven Curtis encourages the use of protocols and dependency injection to enhance testability and adherence to good Swift programming practices.
  • The author provides a subjective assessment that the process described is "awesome" and "wonderful," indicating a positive view of the outlined approach for testing networking code.

A Networking Manager with Alamofire

Can you fully test this? YES WE CAN

Photo by Jesse Roberts on Unsplash

I’ve previously written a network manager with the intention of testing it completely. Can I do the same using Alamofire? That is, test the requisite classes without making real network calls?

Read on to find out!

Difficulty: Beginner | Easy | Normal | Challenging This article has been developed using Xcode 11.4.1, and Swift 5.2.2

Prerequisites:

What the project is, and what it isn’t

The idea of this project is to create a usable network manager using Alamofire, and more than that actually testing the manager, in a similar way to the method I used to create a network manager using urlsession in a previous article.

That means that the View Controller accesses the Network Manager directly simply for ease of creating this tutorial (it is not about architecture!). If you want to learn more about the architecture I might use, tyr this article about MVVM-C that contains a full example — I usually choose to make the network requests from the View Model but you might choose to make these from the coordinator.

The motivation

When I usually create an Alamofire project the request seems all very simple. I'd ususally use Af.request() and it would look something like the following:

Click for Gist

which seems great. But how can we inject a urlsession instance into there? It seems impossible?!?

So let us get started!

The approach

The Alamofire dependency

There are many ways to add Alamofire as a dependency. I’ve created a full guide to this HERE but the short version is that I’ve whacked https://github.com/Alamofire/Alamofire.git to import a new package up to next major version (Using Semantic Versioning).

The Alamofire router

Alamofire has a router in order to compile URL Strings and apply headers. The advantage to using this router is that headers can be put in-place. It sounds fantastic, but I think of this as roughly equivalent to my method of building URLs in Swift.

Like a good Swift progammer I’m going to make this conform to a protocol

so let us see this protocol, which I’ve called APIRouter: note that this file needs to import Alamofire as it uses several types included in the third-party framework.

Click for Gist

We then create a concrete type that conforms to this protocol, I’ve called this JSONPlaceHolderAPIAction because I like catchy names

Click for Gist

Now for fun, (and since testing is always worth doing) I’ve developed the following (rather simple) test. Of note here is that I’ve imported XCTest and Alamofire, but here is the whole file (it passes!).

Click for Gist

Wonderful!

Now for later use I’m going to create a Mock version of the router that can be used later on

Click for Gist

The overall strategy

Usually I’d think about injecting a Session instance which could then be replaces during testing using Dependency injection. However, overriding Session just to inject a URLSessionMock isn't really the strategy that the makers of Alamofire seemed to have in mind.

We can do better

The implementation

The Network Manager

This is going to work with a protocol too (how awesome!)

Click for Gist

Testing

Click for Gist

The ViewController

Yes, you would never do this in production. Yes, you need to handle the errors that are given from the network operation. Yes, we are accessing the NetworkManager straight from the view controller.

In any case, here is my method of creating a View Controller.

Click for Gist

Testing

Now this, of course needs to be tested. There are two possible tests here — I can use the implementation version of the router or the mocked version of the router.

In practice the two are the same because we set the result in a URLProtocolMock which (in my implementation) does not care about the router.

How does that look?

Let us take a break for the URLProtocolMock instance.

The URLProtocolMock

Click for Gist

we then have the option (within our tests) of using an instance of URLProtocolMock to create

Click for Gist

which gives us the session that can then be injected into our NetworkManager instance

Awesome.

Back to testing

We can inject the URLProtocolMock using the ViewController - no problem - here is the excerpt of the test:

Click for Gist

This suceeds, so what about a similar test that will fail? As the requestHandler is a computed property we can just make it throw!

Click for Gist

Fantastico!

Conclusion

The approach taken is rather different than that for a simple URLSession, but it is worth going through this exercise to make sure you NEVER call a real API while testing.

I mean never.

I hope this article has been of use to you, and that you have enjoyed reading it.

The code from this is included in the attached Repo.

If you’ve any questions, comments or suggestions please hit me up on Twitter

Swift
Programming
Software Development
Alamofire
Recommended from ReadMedium