A Networking Manager with Alamofire
Can you fully test this? YES WE CAN
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
- You will be expected to be aware how to make a Single View Application in Swift.
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.
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:
which seems great. But how can we inject a
urlsession instance into there? It seems impossible?!?
So let us get started!
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.
We then create a concrete type that conforms to this protocol, I’ve called this
JSONPlaceHolderAPIAction because I like catchy names
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
Alamofire, but here is the whole file (it passes!).
Now for later use I’m going to create a Mock version of the router that can be used later on
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 Network Manager
This is going to work with a
protocol too (how awesome!)
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.
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
we then have the option (within our tests) of using an instance of
URLProtocolMock to create
which gives us the session that can then be injected into our
Back to testing
We can inject the
URLProtocolMock using the
ViewController - no problem - here is the excerpt of the test:
This suceeds, so what about a similar test that will fail? As the
requestHandler is a computed property we can just make it throw!
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