How do you mock a singleton class

This is a question that came up in my mentorship program Slack team. I think it’s worth a blog post.

I’m trying to set up some tests according to the templates and the new book. I’m struggling with one small detail. In the book, you set up an ordersWorkerSpy, which is derived from a normal class. My question is: What to do if that class is a singleton, which is accessed through something like ordersWorkerSpy.shared?

Let’s work through a code example.

The test subject

Let’s define a view controller that depends on a worker. You’ve seen plenty of examples where the interactors hand off the work to one or more workers. But the worker concept applies equally to presenters and view controllers too. Even a worker can have workers. It’s just a way of delegating complex work to other, smaller components. Breaking up a large class into multiple, smaller classes.

The ViewController class has an instance variable worker that is initialized to the Worker singleton. The viewDidLoad() method calls doSomeWork() which, in turn, calls doSomething() on the worker object. Pretty typical.

The dependency

In the Worker class, the singleton mechanism is done by defining a static constant sharedWorker, and making the default init() private. This has become the simplest, Swiftest way of creating a singleton class.

This works perfectly in the app code. However, in the test target, when we define the WorkerSpy to inherit from Worker, we have to remove the private keyword so that we can override it. If not, the compiler will complain. This is why that line is commented out above. Yes, it’s a tradeoff that we make between enforceability and simplicity. But it’s a small sacrifice in order to keep our test code straightforward to write, as you’ll see next.

The test double

Now we can create the test double in the usual, familiar way.

We make the WorkerSpy inherit from Worker. And then define the sharedWorkerSpy static constant in the same way we did for the test subject. Now, we can override init() to do nothing. The rest is the usual stuff. Override the doSomething() method and record the method invocation.

The test method

Finally, here is the code for the complete test class. But I’m just going to focus on talking about the test method. You can read about how this view controller test is structured and set up in this post.

In the testDoSomeWork() test method, we use the WorkerSpy singleton instead of the Worker singleton – sut.worker = workerSpy. When the test executes, the doSomething() method in the spy will be invoked instead of the real one.

As you just saw, singletons are nothing special in terms of unit testing.

Want to learn more about writing unit tests for your app? Check out my new book – Effective Unit Testing.

Get the Clean Swift Xcode Templates

Subscribe below to get my Xcode templates and learn how to apply the VIP cycle to your projects, extract business and presentation logic into interactor and presenter, navigate to different scenes using multiple storyboards, and write fast, maintainable tests with confidence to make changes.

I promise I'll never send you spam. You can unsubscribe at any time.

Raymond
I've been developing in iOS since the iPhone debuted, jumped on Swift when it was announced. Writing well-tested apps with a clean architecture has been my goal.

1 Comment

  1. By setting init() as non-private class becomes non-singleton,
    nothing stops developer to use it (init) directly in the code later on.

Leave a Comment

Your email address will not be published. Required fields are marked *