Savvy software comes from savvy people.

Test-Driven iOS: Dependency Injection

In the last Test-Driven iOS post, we talked about setting up our code and test files to allow unit testing on storyboarded apps. I mentioned that an extension of that setup could be used to take control of dependency injection inside of our apps. We’ll talk about that today.

Suppose our ExampleViewController required a service to make a network call:

Here, we want to inject our DependableService. There are two methods featured above: the loadFromStoryboard() class method that we introduced last time and an initializer, which extends an initializer provided by the UIViewController class.

When the OS instantiates this view controller, it does so by using the initializer that you see above. This allows us to use it to set a field variable for our service from a shared environment that we set up for our app.

That same field variable is set to the parameter value we have added to loadFromStoryboard(). In our tests, we will call the loadFromStoryboard() method directly, so the initializer that gets our service from the shared environment is never hit. Instead, we can set up a fake version of our service like so:

And then we can use it to assert that we interacted with that service the way that we expected.

It’s worth noting that Swinject can be used to handle dependency injection for you. I have found its setup to be no less cumbersome than writing a minimal solution like this one. That having been said, the framework may be more suitable if you have more complex dependency injection requirements.