TL;DR: You can inject code dependencies in a transparent way by using generics and typealias.
Writing code in Swift is fun. Sometimes so much fun that we forget about an important detail.
Writing Tests. Yes. Sometimes we forget to write tests. We’ve all been there. Honestly.
What to do?
First of all - you open the existing empty test file and begin to implement a test.
But soon you will notice it’s not that easy to test your code independently.
Many parts are intertwined. You realize - you better get some kind of dependency injection.
In software engineering, dependency injection is a software design pattern that implements inversion of control for resolving dependencies. A dependency is an object that can be used (a service). An injection is the passing of a dependency to a dependent object (a client) that would use it. The service is made part of the client’s state. Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.
Basically what we want to do is - while testing one part of the code base - we want to fake (mock) the rest of the codebase.
Other ecosystems have some well-established techniques for this, like e.g. Spring on the JVM. Spring provides different flavors (like e.g. XML or Annotations) to glue your code together. Plus it provides an easy way to change that configuration for a test.
Turning to Swift:
Swift is not yet 2 years old. We are still searching for best practices. Every single application is different, thus the way I propose Dependency Injection may not work for every kind of application. Let’s start at the beginning.
You should google “Dependency Injection Swift” now to read about some common patterns.
First there was an item 📦
We will build a really simple use case and add some more complexity later on. Say hello to:
The two structs look pretty straight forward. In real life, however, this is really hard to test.
Because we use: let now = NSDate().
Every time we call NSDate() it returns the NSDate of now.
Let’s imagine the following situation:
we have a list of items, which are valid when the item’s date is after now
we want to ensure that only valid items are returned from the model
furthermore we want to check the valid items 10 minutes later
and maybe it is important for us to check the same model a day later
Freeze the world ❄️
Our first approach is to use a global func for getting now.
Pro: super easy to implement
Con: feels clunky + if we forget to reset the timeShift … 👹
Come on, we will never ever forget to reset the timeShift because we wrap it like:
The world outside ☎️
Btw. time to add complexity: Did you know that valid items have to pass through a web API before they can be returned? Me neither.
Seems like we will have to implement a WebService and pass the valid items to the server for final verification. The WebServicewill be called in the model. But wait - when writing tests for the model we need to be able to mock the service.
Easy, therefore we initialize the model withWebService.
Have a look:
Pro: super flexible and decoupled
Con: feels clunky
And to be honest: There is too much code in the model only related to dependency handling. Too much noise. The more dependencies you get, the more code you write for managing them inside your model. The ModelGlobalhad 8 lines of code.
(w/o web service - fair enough), while ModelInjectednearly tripled that.
I am not sure if I want to do that.
Plus think about:
Those dependencies are loosely coupled, but in my opinion they belong together in a semantic way. I even go one step further and claim:
The dependencies are the world your model lives in.
There is a Real World, a Mocked World and maybe many many more.
Parallel Worlds 🌓
When our model is able to exist in parallel worlds, it might be a good idea to define it as generic type. But what kind of boundary shall we use for the generic type?
Next up - we create the Real World:
Only one step is missing to see the end result:
An environment-aware model that:
returns validItems with date after nowof environment
has a sync func that provides validItems to the web service of the environment, logs the server result (logging service of environment) and calls the callback with the corresponding items
We end up with:
a generic ModelFor taking a type conforming to Environment
a typealias Modelrepresenting ModelFor<RealWorld>
no code necessary to manage the dependencies with properties or whatever
All you write is:let modelNow = Model(items: items)
What about mocking?
We define a couple of different worlds and name them:
The clear naming immediately gives an idea what kind of dependencies are used. And suddenly writing tests and changing conditions is a no-brainer.
What does the output look like:
-> RealWorld has 10
-> WorldInTenSeconds has 9
2016-05-21 11:30:10 +0000 Returned true
after accessing server 9
-> WorldYesterday has 10
2016-05-20 11:30:00 +0000 Returned true
after accessing server 10
-> WorldTomorrow has 0
2016-05-22 11:30:00 +0000 Returned true
after accessing server 0
-> WorldYesterdayOffline has 10
2016-05-20 11:30:00 +0000 Returned false
after accessing server 0
Even the time output of the logging service is changing according to the Environment.
no need to manage global state
no need to explicitly inject dependencies
no need to use the generic type in the code: just use the typealias
Refactoring looks like:
In my opinion the generic approach is a lean Swift solution for a well known problem. Of course you can argue that it is a little bit verbose when you already use a generic model. But looking ahead: Swift 3 will give us generic typealias - so no need to worry.
Interested in playing around? Here we go with a full playground: