mamcx
mamcx

Reputation: 16186

How unit testing a singleton in obj-c?

I have a singleton class, and can't unit test their code.

I have test like:

    Db *db = [[Db alloc] initWithName:@"sample.db"];

[db createDb];

STAssertEquals([db existDb],YES,@"The db is not created!");

But only work the first. When the second is executed, I always get "null" from the initWithName method. When I remove the singleton support code, all work as expected.

I could hack the testing (but I don't know how right now) but wonder if exist a "poper" way to deal with this.

The singleton is located here: http://code.google.com/p/chibiorm/source/browse/trunk/src/Db.m

Upvotes: 1

Views: 961

Answers (5)

Satakshi Pandey
Satakshi Pandey

Reputation: 432

@implementation Singleton

+(nonnull instancetype)sharedInstance {
    static Singleton *sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });

return sharedInstance;
}

- (instancetype)init
{
    self = [super init];
    return self;
}
@end

Test Class - expose the init method to instantiate

@interface Singleton (Test)

- (nonnull instancetype)init;

@end

Upvotes: 0

George Cook
George Cook

Reputation: 180

The guy here has written what I would have written as the answer, which is use categories to provide differnet instances from your singleton methods as needed.

http://twobitlabs.com/2011/02/mocking-singletons-with-ocmock/

The other guys above who are all pontificating their opinions of "singletons are bad m'kay" should be ignored. The first thing I did when I switched to objective c was write an iOC library, which I never used because I realised that objective c is NOT java, so please ignore people who bleet on about about java dogma : it doens't always apply to objective c.

Upvotes: 0

Tilo Prütz
Tilo Prütz

Reputation: 1814

I think you shouldn't return nil on the second alloc but raise an exception. If you want to use a singleton you should not try to create two :).

However, if I decide to create a singleton my class looks like:

@implementation MySingleton

static id _instance = nil;

+ instance
{
    if (_instance == nil) {
        // alloc/init
        _instance = [[self alloc] init];
        …
    }
    return _instance;
}
…
@end

As you can see I am not enforcing that there may never be more than one instance. Instead I am using the convention to get the instance only with the instance method.

Upvotes: 0

lothar
lothar

Reputation: 20237

Maybe you could use the Factory pattern and create a factory that hands out only one instance (effectively your singleton). Then the implementation is not a singleton and you can unit test it to your hearts content.

The only drawback is that you are not protected by the language to create your own instance if you don't retrieve it from the factory. In C++ you may overcome this by making the constructor private and the factory and the unit test friends. I am not sure if Objective-C has a similar feature.

Upvotes: 0

Kyle W. Cartmell
Kyle W. Cartmell

Reputation: 683

Singletons are hard to unit test and are sometimes the result of poor design.

My recommendation would be to think hard about whether you really need a singleton in the first place.

Upvotes: 2

Related Questions