Testing Singletons

A topic that keeps coming up in iOS development is the use of Singletons. I had thought that I had written about these awhile back, but now realize that I covered these in the Testing Singletons chapter of the Unit Testing iOS Applications with Xcode 4 lynda.com course, but not written about them here. The mention of Xcode 4 probably keeps folks from watching this video, now that Xcode 5 is out. So I’m going to repeat some of what was said, but encourage you to watch the video anyways. Much of what is covered in that course is still very relevant.

Unrestrained, and used without discipline, Singletons can be evil. Apple uses them a lot, so it’s easy to see why developers would think that it is perfectly ok to use them a lot also. I’m not going to get into the dangers of using Singletons, other than to point out that they are essentially just global variables. There are plenty of resources available describing the reasons to be wary of using global variables, so I won’t belabor the point here.

What I will address here is the difficulty of testing them. Singletons break one of the assumptions made when using the standard pattern of Arrange-Act-Assert. The Arrange step assumes a clean starting point for every test. Each test should be completely independent of every other test. But by design, singletons retain state. In the case of unit tests, this means that one test can leave state altered that might affect another test, and that’s not good.

A pattern that can be used to make singletons more easily testable is called Singleton By Choice. In this pattern, singletons can be created and used as described by the singleton design pattern, but without forcing the use of singleton behavior upon the tests.

By design, Singletons are intended to be accessed using the sharedInstance class method, or sometimes a similarly named class method like shareManager. But what happens if the Singleton class is created using the usual alloc init approach? (eg. [[MySingleton alloc] init]).

In Objective-C, it is common to see Singletons implemented in a manner that prevents making this mistake. This is done by overriding allocWithZone or init and redirecting back to the sharedInstance class method.

Before doing that though, stop and think about why you are doing it.  While well intentioned, this type of behavior not only runs contrary to the TDD approach, it in fact makes it difficult to write unit tests. The assumption is that “some other programmer is going to mistake this singleton for a normal object, so let’s fix this potential future error by silently redirecting the wrong method call to the right one”.

Ok, lots of problems with that assumption, including:

  • New code won’t be done using unit tests
  • New code may break existing code, and the existing unit tests won’t catch it
  • or maybe existing unit tests won’t ever be run
  • I’m the only programmer that will work on this code that knows what I’m doing, so I better fix the other programmer’s mistakes now, while I can.

In a TDD world, we really don’t need to write code that is this defensive. It’s actually kind of offensive, pun intended.

Go ahead and create a singleton when appropriate, use the sharedInstance method in all your product code, and use [[mySingleton alloc] init] in your unit tests to get a fresh copy to test.

And finally, you’re using a simple singleton parent class so that you don’t have to rewrite it and its unit tests every time, right? If not, here’s one, or you can get it from Github:

//
//  SingletonByChoice.h
//
//  This class provides basic Singleton By Choice functionality.
//  It is intended for use as a parent class for singletons.

#import <Foundation/Foundation.h>

@interface SingletonByChoice : NSObject

+ (instancetype)sharedInstance;

@end
//
//  SingletonByChoice.m
//
//  This class provides basic Singleton By Choice functionality.
//  It is intended for use as a parent class for singletons.
//

#import "SingletonByChoice.h"

@implementation SingletonByChoice

+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken = 0;
    
    __strong static id _sharedObject = nil;
    dispatch_once(&onceToken, ^{
        _sharedObject = [[self alloc] init];
    });
    return _sharedObject;
}

@end
//
//  SingletonTests.m
//

#import <XCTest/XCTest.h>
#import "SingletonByChoice.h"

@interface SingletonTests : XCTestCase
@end

@implementation SingletonTests

#pragma mark - helper methods

- (SingletonByChoice *)createUniqueInstance {

    return [[SingletonByChoice alloc] init];
    
}

- (SingletonByChoice *)getSharedInstance {
    
    return [SingletonByChoice sharedInstance];
    
}

#pragma mark - tests

- (void)testSingletonSharedInstanceCreated {
    
    XCTAssertNotNil([self getSharedInstance]);

}

- (void)testSingletonUniqueInstanceCreated {
    
    XCTAssertNotNil([self createUniqueInstance]);
    
}

- (void)testSingletonReturnsSameSharedInstanceTwice {
    
    SingletonByChoice *s1 = [self getSharedInstance];
    XCTAssertEqual(s1, [self getSharedInstance]);
    
}

- (void)testSingletonSharedInstanceSeparateFromUniqueInstance {
    
    SingletonByChoice *s1 = [self getSharedInstance];
    XCTAssertNotEqual(s1, [self createUniqueInstance]);
}

- (void)testSingletonReturnsSeparateUniqueInstances {
    
    SingletonByChoice *s1 = [self createUniqueInstance];
    XCTAssertNotEqual(s1, [self createUniqueInstance]);
}

@end

To create your own SingletonByChoice subclass, simply override SingletoneByChoice. Since sharedInstance returns instancetype, it will return your subclass type

@interface MyClass : SingletonByChoice

// Your methods and properties ...

@end

@implementation MyClass

// Your implementation here
// Note that you don't need to override sharedInstance

@end

4 thoughts on “Testing Singletons

  1. Hello,
    This is great. We are trying to get our first iOS project at work much more code-covered. We have several singletons in use, each one implements that same shareInstance logic. I am applying your parent singleton class here, but must be missing a certain part.

    The children singletons now inherit the SingletonByChoice parent class. Is it a correct assumption that the children’s sharedInstance method would just called [super sharedInstance] ? That doesnt seem to work. The logic also doesnt make sense to me. The child’s sharedInstance wants to return that child class name. Or should all the children return a SingletonByChoice? That doesn’t make sense either, because they need to be differentiated.

    Obviously Im missing something simple here. Thanks for your time and any hints in the right direction.

    1. The children shouldn’t need to override sharedInstance. They should be able to just use the parent’s. To do this without having to cast the reference, use jowie’s suggestion and change the return type of the parent sharedInstance to instanceType:

      + (instancetype)sharedInstance;

  2. Hi!

    I am fairly new to programming and developing an app but I am having a little trouble understanding the hierarchy of how this works. So if MyClass is the class I want to be a singleton and inherits from SingletonByChoice, will my only instance of MyClass be of type SingletonByChoice? If I wanted to create multiple instances of the class for testing how would I declare that? Could you possibly give an example of how the inheritance works with a class?

    Sorry about my confusion, I am fairly new to the field. Thank you!

Leave a Reply