iOS Unit Testing: Dependency Injection with Structs in Swift

Dependency Injection is a really important unit testing technique that makes your code better by exposing the dependencies very clearly. I wrote a blog post about dependency injection here. If you need to learn more about dependency injection, make sure to read it before moving forward with this post!

One big issue with dependency injection in Swift is that it often requires subclassing to work (see my previous blog post as an example). That conflicts with the ideal of using value types in Swift, since you can’t subclass a struct! So while I usually started out by making something a struct value type, I often ended up having to make it into a reference type so I could subclass it for dependency injection in my tests. It was really frustrating.

However, after this years WWDC session on Protocol-Oriented Programming (POP), I finally understand how to get rid of subclassing and still use dependency injection in my tests! The code is much better as a result 🙂

Before POP

So let’s take a look at the MinionService I had in my previous blog post on dependency injection:

The reason MinionService is a class is so I can over-ride the implementation of getTheMinions in my ViewController tests:

After POP

Using POP, since getTheMinions is the method I need to be able to have my own implementation of for testing, I can extract it out into a protocol and change my MinionService to a struct that conforms to that protocol!

So my fake_MinionService in tests doesn’t have to subclass my MinionService. It can just conform to my protocol!

My fetchMinions method in my ViewController now just takes in any object that conforms to the MinionGettable Protocol:

The tests stay the same!

Enjoy the article? Join over 20,000+ Swift developers and enthusiasts who get my weekly updates.

  • Uros

    Indeed interfaces are known to go hand in hand with unit testing due to their nature. They come super handy when mocking/stubbing. Further on, interface/protocol driven programming leads to good code in most of the cases. Thanks for sharing.

  • You can create test fakes only on the basis of protocols and classes, since subclassing structs won’t yield the expected result. In practice, this means I create a lot more classes by default and use structs only for value types that represent data instead of what I initially wanted, namely using structs by default. That’s because I don’t feel too thrilled about creating protocols for everything just in case. Since I test thoroughly, there’s virtually no exception.

    This makes me sad a bit, but it works and limits the scope of structs to something tangible: data. No weird surprises if Banana is a struct or a class.

    • pipacs

      Agree, this is sad but still feels like a better advice than inventing weird protocols like “MinionGettable” for the sake of unit tests.

  • Irfan Lone

    Hi, I am still new to swift , so please apologize. In the last section of the code, line 2
    Shouldn’t minionService.getTheMinions { .. } be minionService().getTheMinions { .. } Instead
    or else you would have to use static keyword for the func.

    If that seems right, can you please explain the difference, I am little confused over the usage of making static functions in structs over normal functions?
    Thanks in advance

    • guest

      “minionService” is the instance passed into the function in line 1. (Note the camel case. Usually instance variables begin with a lower case and types / class names, etc. begin with an upper case.)

      A static function would be a function on the type, not an instance, so you wouldn’t have the parentheses anyway. Static functions for structs are like class methods for classes.