Making a testable singleton (with a lowercase s)

Amber Spadafora
2 min readFeb 12, 2022

--

An example on how to create a testable singleton object and use property injection to replace it with a mock object during testing.

The Singleton pattern restricts the instantiation of a class to one instance.
In swift they commonly appear as a class with a private initializer and a static constant named shared, that holds an instance of itself.

example of using the Singleton pattern to create an api client
An example of using the UntestableApiClient singleton in a view controller

The code above makes it impossible to isolate the view controller from its dependencies.

In the view controller’s loadData method, we are directly accessing a shared instance of UntestableApiClient. In testing, we would not be able to test the view controller without being impacted by the ApiClient i.e we cannot test the view controller in isolation of its dependencies.

If we re-design the api client singleton to be an open class, with a public initializer (which breaks the law of being a Singleton with a capital S), then we can mock the api client during testing. In order for this to work we also need to inject the dependency into the view controller.

Re-designing the code to be testable including demoting our api client from a Singleton to a singleton (by removing final from its declaration, and removing private from its initializer), and using property injection to pass a api client to the view controller, instead of accessing a global instance
An example of how you would mock the api client during testing
An example of how you can now test the view controller in isolation of it’s api client dependency

TIL; true Singletons in swift cannot be tested using subclassing, but singletons (in the way that URLSession is a singleton) can be written in a way that they can be testable using subclassing and property injection

--

--