Swift - Testing singletons

In this post we will see how you can test swift singletons easily.

Disclaimer: I haven't discovered it myself, but I found it a while back on StackOverflow while looking for how to test Swift singletons. So thanks to the anonymous person for the wonderful solution

Say, I want to test the following class which takes singleton instance as a dependency and performs necessary operation. We can easily use the inheritance to extend the existing singleton and override methods if necessary or just keep it calling the parent methods.


// Singleton class.
class SharedUtility {
    static let sharedInstance = SharedUtility(100)
    
    var val: Int
    init(_ val: Int) {
        self.val = val
    }
    
    func doThis() {
        print(self.val)
    }
}

// Class which takes singleton as a dependency.
class SingletonDependable: NSObject {
    var shared: SharedUtility
    var simpleVariable: Int = 2
    
    init(sharedInstance: SharedUtility) {
        self.shared = sharedInstance
    }
    
    func doSomething() {
        shared.doThis()
        simpleVariable = simpleVariable * shared.val
    }
}

Actual testing.

In order to test we will extend our singleton class SharedUtility and add dummy implementation for method func doThis() if necessary, but maybe not.


class DummySharedInstance: SharedUtility {
        
        override init() {
            super.init()
            self.val = 100
        }
        
        override func doThis() {
            print("Printing dummy value for variable")
        }
    }

And then we can use it as a dependency to pass to the initializer of our class SingletonDependable


class SingletonFileSpec: QuickSpec {
    override func spec() {
        describe("Testing singleton object") {
            it("Singleton should output value as expected", closure: {
                let dummyInstance = DummySharedInstance()
                let dependable = SingletonDependable(sharedInstance: dummyInstance)
                dependable.doSomething()
                expect(dependable.simpleVariable).to(equal(200))
            })
        }
    }
}

Here we have tested our singleton successfully. As I mentioned, in general case it should be enough to just extend the existing singleton instead of also providing with dummy method.

However, if your singleton is responsible for doing things which may not be suitable during testing, you can easily mock those methods.

(e.g. Some expensive tasks with expected results or network calls. In latter case, network calls can easily be mocked by reading similar data from local json file)