Introduction

Nowadays, unit testing is an important topic. There is no doubt that we should test our code as much as possible to avoid bugs. Unfortunately, unit testing is not always easy since we may have very complex objects with a lot of dependencies.

In this article, I want to show you a couple of approaches which I use to mock the dependencies of my objects to keep the tests as plain as possible.

For the sake of explanation, fetchUserName is a synchronous method. If we have to send API requests, we should always perform asynchronous operations to avoid blocking the main queue.

If we want to test ViewModel, we can consider:

ViewModel: the system under test.

Interactor: a dependency since ViewModel uses it to get a user name.

Mock the dependencies

Why should we mock the dependencies in our unit tests?

If we don’t have a clear understanding of the answer to this question, we may risk writing wrong tests.

Unit testing is born to test the behavior of a single component—SUT—without caring of its dependencies. The purpose of mocking the dependencies is to keep the SUT isolated from external objects.

If we consider the example used in Mock the dependencies, we may want to test that the computed property userName of ViewModel uses the method fetchUserName() of its interactor. The important thing to understand here is that we want to test only if ViewModel calls fetchUserName(). We don’t care of the actual result of fetchUserName(). We don’t need to test if fetchUserName() works because we’ll test it in another test suite where we have Interactor as SUT.

The first step to mock the dependencies is finding a proper way to inject them. Once we are able to inject a dependency, we’ll be able to inject a fake dependency for testing purposes. I use mainly two approaches: Instance Injection and Metatype Injection.

Instance Injection

This kind of injection is the most common. We inject the instance of a dependency using a parameter of a method or constructor.

The first step to use this injection is abstracting the dependency. We can achieve it with a protocol.

We can continue using the example used in Mock the dependencies and we can abstract Interactor with a InteractorType protocol:

1

2

3

4

5

6

7

8

9

10

11

protocolInteractorType{

funcfetchUserName()->String?

}

classInteractor: InteractorType{

funcfetchUserName()->String?{

letusername=// Result of a API request

returnusername

}

}

Now, we can change the ViewModel constructor to accept the new protocol as dependency:

1

2

3

4

5

6

7

8

9

10

11

12

classViewModel{

private letinteractor:InteractorType

varuserName:String?{

returninteractor.fetchUserName()

}

init(interactor:InteractorType){

self.interactor=interactor

}

}

Since Interactor conforms to the new protocol, we can still instantiate the ViewModel with an Interactor instance. We can also use a default parameter value to omit the interactor parameter like this:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

classViewModel{

private letinteractor:InteractorType

varuserName:String?{

returninteractor.fetchUserName()

}

init(interactor:InteractorType=Interactor()){

self.interactor=interactor

}

}

letviewModel=ViewModel()

At this point, we’ve just created a new instance injection of Interactor and we are ready to use it for testing. The first step is creating a new mock interactor which conforms to InteractorType:

Now, we are finally ready to inject a mock dependency to test our SUT and its userName property.

1

2

3

4

5

6

7

8

9

functest(){

letstubInteractor=StubInteractor()

letsut=ViewModel(interactor:stubInteractor)

_=sut.userName

XCTAssertTrue(stubInteractor.isFetchUserNameCalled)

}

We use _ = to discard the value returned by sut.userName. We don’t care of the value but just if we call fetchUserName in the computed property.

Metatype Injection

Instance injection doesn’t suit every situations. We may have some cases where we don’t want to inject the instance of a dependency.

The strategy of Metatype Injection is using the dependency types which we want to use. In this way, we don’t need an instance but just the type of dependency.

We can continue using the example used in Instance Injection. We’ll see a real scenario at the end of this section.

Let’s consider that we no longer want to inject an instance of Interactor but we want to instantiate an Interactor object inside the ViewModel constructor:

1

2

3

4

5

6

7

8

9

10

11

12

classViewModel{

private letinteractor:InteractorType

varuserName:String?{

returninteractor.fetchUserName()

}

init(){

self.interactor=Interactor()

}

}

With this implementation, we wouldn’t be able to inject any dependencies. For this scenario, we can use a Metatype Injection.

First of all, we can create a Configuration struct inside our ViewModel with the dependency types to use:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

classViewModel{

structConfiguration{

letinteractorType:InteractorType.Type=Interactor.self

}

private letinteractor:InteractorType

varuserName:String?{

returninteractor.fetchUserName()

}

init(){

self.interactor=Interactor()

}

}

By default, we set Interactor.self as interactor dependency type to omit it when we instantiate Configuration.

If you don’t understand the meaning of:

1

2

letinteractorType:InteractorType.Type=Interactor.self

InteractorType.Type means that the property interactorType will not store an instance but just a metatype of InteractorType. Interactor.self returns the type of Interactor.

Swift allows us to instantiate a new object using its type as variable. The only constraint is that we have to use the method init() explicitly. It means that the following code would throw a compiler error:

1

2

letinteractor=interactorType()

Instead, we should write:

1

2

letinteractor=interactorType.init()

In our example, interactorType is a property of type InteractorType.Type to keep the type abstract. Therefore, we wouldn’t able to call its init method since the protocol doesn’t have it. We can solve this problem adding a init method in the protocol like this:

1

2

3

4

5

6

protocolInteractorType{

init()

funcfetchUserName()->String?

}

Then, we must add the init method also in Interactor, which implements the protocol:

1

2

3

4

5

6

7

8

9

10

11

classInteractor: InteractorType{

required init(){

}

funcfetchUserName()->String?{

letusername="// Result of a API request"

returnusername

}

}

Remember to use the keyword required before the init to indicate that every subclasses of Interactor must implement that constructor.

The last step is injecting this configuration in the ViewModel constructor:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

classViewModel{

structConfiguration{

varinteractorType:InteractorType.Type=Interactor.self

}

private letinteractor:InteractorType

varuserName:String?{

returninteractor.fetchUserName()

}

init(configuration:Configuration=Configuration()){

self.interactor=configuration.interactorType.init()

}

}

letviewModel=ViewModel()

We use Configuration() as default parameter to omit it when we create a ViewModel object. In this way, the default interactorType value is Interactor.self. We’ll see in the tests how to inject a mock dependency.

We’ve just completed the implementation. Now, we can focus on the tests.

With this approach, the tests are a little bit tricky. With Instance Injection, we can inject an instance of StubInteractor. With Configuration Injection, we cannot inject an instance but just a type. For this reason, when we write the tests, we must use static properties to check our test results.

We must use static because a static property stores an information without using an instance and its lifetime is the entire run of the application.

We can refactor StubInteractor to use the static information:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

classStubInteractor: InteractorType{

private(set)staticvarisFetchUserNameCalled=false

required init(){}

funcfetchUserName()->String?{

StubInteractor.isFetchUserNameCalled=true

return"Test"

}

staticfuncclean(){

StubInteractor.isFetchUserNameCalled=false

}

}

The method clean is very important to clean all the static information used in the tests. Since we are using static properties, our application shares the value of isFetchUserNameCalled everywhere—like a Singleton. Therefore, If we don’t clean isFetchUserNameCalled, it would remain true also in the next tests.

Remember to call the clean method in the tearDown to clean all the static information for the next tests.

If StubInteractor has a lot of properties to store the test results, the method clean would have a lot of properties to clean. We can avoid it using a Singleton for StubInteractor.

We can restore isFetchUserNameCalled from static to a normal property. Then, we must add a new static property:

1

2

private(set)staticvarshared:StubInteractor!

which will be the access point of our singleton object. Then, we set the shared value internally in the constructor:

1

2

3

4

required init(){

StubInteractor.shared=self

}

And, finally, we can clean our singleton in our clean method:

1

2

3

4

staticfuncclean(){

StubInteractor.shared=nil

}

The final version of StubInteractor should be like this:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

classStubInteractor: InteractorType{

private(set)staticvarshared:StubInteractor!

private(set)varisFetchUserNameCalled=false

required init(){

StubInteractor.shared=self

}

funcfetchUserName()->String?{

isFetchUserNameCalled=true

return"Test"

}

staticfuncclean(){

StubInteractor.shared=nil

}

}

Thanks to this refactor, in our test assert, we can use our new Singleton to test the value of isFetchUserNameCalled:

1

2

XCTAssertTrue(StubInteractor.shared.isFetchUserNameCalled)

This is the Metatype Injection. You may argue that it’s complex and tricky with a Singleton. I agree that we should go with Instance Inject as much as possible but, unfortunately, I’ve found some situations where with a configuration the code would remain cleaner.

Here, we have an implementation of a delegate—UsersListNavigationDelegate—where we create a new UserDetailsViewPresenter object using a user and a navigation delegate as parameters. This object would be quite difficult to inject from outside keeping the code clean. In this case, a Metatype Injection would be much better.

Another real scenario is the Core Data stack. If we have a class which manages a Core Data implementation, we cannot inject the instances of the stack objects from outside in a clean way since they are tightly coupled. You can see how to inject the Core Data stack with Metatype Injection in the StorageKit class CoreDataStorage.

Conclusion

I used a Configuration struct for Metatype Injection to keep clean the approach. Of course, we can use the same Configuration struct approach also for Instance Injection.

I usually try using Instance Injection as much as possible to avoid using static variables in my unit tests. I created Metatype Injection to solve some injection problems after trying different approaches. If you have better approaches or you want to share your knowledge, please leave a comment, it would be greatly appreciated. Thank you.

Share this:

Related

Marco Santarossa

Hi there, I'm Marco and I'm an Italian developer. I moved to London in 2016 to work at Sky as iOS developer.
I've been an iOS Developer since 2011 and I sometimes write embarrassing PHP/JS code.
I'm keen to learn new things and I spend most of my spare time learning as self-taught.
When I don't develop, I like watching MMA fights and cooking Italian food.