Using dependency injection to test an UIViewController
Hi everyone, is it very common when we start coding to create objects that needs others objects to work e.g. in a Car
class that need a Person
object to start the engine and drives ( starEngine
and drive
are hypothetical methods that needs a Person
instance). So we can say that a Car
needs a Person
to work.
The code above shows us that Jhon is driving our car, and only Jhon can do it! Because every Car
object will create an instance of a Person
named Jhon. But what if Mary wants to drive? How can we rewrite that code to let Mary drive our car?
We can pass a Person
to our Car
, making it work for any instance of Person
, in other words: reusable. We don't care how our Person
object is created, if it's created by a factory, another class or if it needs a name, age or any other dependency, we just need a Person
instance.
In software engineering, dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object. 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.
Let's turn this example in a daily problem. In the code below, our ViewController
uses a ListService
object. This ListService
loads data from a web service but what if we want to load data from a local database, a file or somewhere else? Do we need to create another ViewController
? And if we want to test this ViewController
how can we make the API response's to be always the same so we can validate our test?
Now think about testing this class we'll need at least an internet connection and a stable response every time we run our tests. This makes our test slow and impracticable. The solution in to use dependency injection to use a custom mocked ListService
object.
One way to do that is to create an interface that has a load
method and implement it in custom objects. In other words, we create a Service
protocol and objects that know how to load data from web servers, database, files, etc.
With this approach, we can easily create different kinds of Service
to use. E.g. one service loads data from a web service, the second one from a local database and the last one is mocked so we can use it for testing our ViewController
without the need of a real API response or data in our local database.
Every kind of Service
object can have unique dependencies. Our WebServiceListService
will need to know how to make a request, so it will need a Request
object. DataBaseListService
needs a database connection to load data. Theses unique dependencies are invisible to our ViewController
. It just needs a Service
object that has a load()
method.
To test our ViewController
we just need to create a MockListService
and inject it during our tests.
Injecting objects makes our code decoupled and easier to be tested. This can be achieved by passing objects via constructors, setters or interfaces.
Ps: If you like this post, share it on twitter, recommend it on medium, or both =). This really helps me to reach more people. Thanks a lot.