Dependency Injection + ServiceLocator Pattern + Unit Testing with Self Created Mocks in Swift

Keran Marinov
4 min readJul 15, 2018

If you are finding the title interesting then let’s dig in and find out.

Dependency injection is a technique where we supply required dependencies to an object either through initializer, setter method or just by setting the value of the property.

In this article, we will concentrate on dependency injection through initializer. One of the main issues of this approach is that as the dependencies increase, a class will end up with lots of parameters in the initializer, and the challenge of testing with mocks might become overwhelming.

Let’s assume we have an app with MVVM architecture where every view controller has a view model and this view model might have quite a few dependencies. Typically below the ViewModel layer, there is a service layer where the business logic is handled or things like read-write to disk, API requests etc.

Starting with an example, let’s assume we have UserViewModel with 3 services injected through initalizer, it would look like this.

As the time goes, the dependencies of the UserViewModel will increase, managing those service mocks for testing will become quite a bit of a challenge.

The obvious solution is to have some kind of mechanism where the dependencies are created from a single point throughout the whole app, this is where Service Locator pattern comes into place, it provides a way to create any type of dependency such as Services, utility classes etc..

There are variations of service locator pattern used in Java world by utilizing singletons, registering classes, interfaces etc..

But here we will use a Swift approach (hopefully…) by using some protocols and default implementation for creating our dependencies.

In order to create our own Service Locator pattern, we will concentrate on how can we use this pattern in our testing, add our mocks on the fly while testing our UserViewModel.

We will call our class that creates all these dependencies Creator. As you can see below our creator also extends a protocol called Creatable.

Creatable is even more interesting because it just extends two other protocols called ServiceCreatable and UtilityCreatable.

ServiceCreatable is just a protocol with default method implementation in the extension that creates a service with generic type S.

S is bound to a protocol called Service (there is a sample project you can have a look at Service Protocol later on).

This will create all our services that extend the protocol called Service. The reason why create method takes the parameter Creatable is for injecting the Creator into any Service that needs to create some other services, but this will become clearer once we see the whole picture.

UtilityCreatable is just a protocol, too. It creates a class called AnalyticsTracker which also takes Creatable in the initializer, this means AnalyticsTracker will create some other dependencies when initialized, but looking at HttpClient, it does not need the Creatable, it just simply calls the default initializer and returns the newly created HttpClient instance.

Basically, through simple protocol composition, we have just created our ServiceLocator pattern called Creator.

Let’s see how the Creator class is being injected to the UserViewModel and how it is being used in the UserViewModel initializer.

As you can see all we have to do is to provide a new instance of Creator to the UserViewModel initializer, and by doing this now the UserViewModel has access to every dependency that was being declared in the Creator’s protocols.

You can simply initialize a service by calling create method.

userService = creatable.create(with: creatable)

And this is how the UserService initializer would look like.

Again UserService just uses the Creator provided and creates its own dependencies such as AnalyticsTracker and HttpClient.

So far we have created a Service Locator Pattern called Creator with few simple steps. Creator is a concrete class that extends a protocol called Creatable.

Creatable is composed of two other protocols called ServiceCreatable and UtilityCreatable. This is the abstraction of our ServiceLocator Pattern, as the application grows we might need more protocols in order to accommodate different types e.g. RouterCreatable, DaoCreatable etc…

But feel free to simplify your abstraction by using generics etc.. It is totally your choice.

Ok, You have made it, thus far. Now let’s see if what we have created so far, will make the testing easier or not. Since the promise of this article was to make unit testing and mocking easier.

There is one more class called MockCreator, that might be challenging to understand at first but don’t worry there is a sample project at the end, hopefully, the whole picture will make sense.

I will try to explain the aim of this class but don’t worry if it does not make sense at first. Basically, MockCreator just like the Creator extends the Creatable and provides some methods that override the protocol’s methods of ServiceCreatable and UtilityCreatable and by doing so, now you can replace the real classes with your mocks automatically.

Let’s see the UserViewModelTests. As you can see, we initialize our MockCreator in the setUp but rather than the real Creator we inject the MockCreator to the UserViewModel initializer.

Because our MockCreator overrides composing Creatable’s ServiceCreatable and UtilityCreatable protocols methods by providing implementation, it injects the mock classes rather than the real classes.

In your tests, we can access those added mocks with the following syntax

creator.find(MockUserService.self).mockName = "Neo"

MockCreator has a find method where we can access our mocks with the type provided in this case it is called MockUserService.self which is the mock class of the UserService.

Basically, just by passing the MockCreator to UserViewModel, all the mocks are added automatically in a dictionary of MockCreator and you can access all the defined mocks like below.

creator.find(MockUserService.self)
creator.find(MockAccountService.self)
creator.find(MockFriendService.self)

I hope it all makes sense, once you look at the source code of the example project. https://github.com/k-marinov/di-app.

Thanks for reading.

Now sit back and relax, remember Immutability changes everything.

--

--