Archive

Archive for June, 2009

Cocoa unit testing with OCUnit and OCMock: Mocking out IBOutlets

June 27th, 2009

I’ve been doing Objective-C (Cocoa) development for OS X at work now for just under a month, and I’m probably going to start pasting some useful snippets here on the blog.

One thing that I had some issues getting to work when I started writing unit tests for my code was IBOutlets for objects that were instantiated through the interface builder. These will only have their bindings setup when initializing the class with loadNibNamed from NSBundle or something similar. This is a bad idea during unit tests since it makes your tests depend on other classes. It is also rather slow.

Instead it’s better to just inject the IBOutlets manually.

The starting point

Let’s pretend I have a class called GameEngine whose interface and implementation looks something like this.

// GameEngine.h
@interface GameEngine : NSObject
    {
        // Handled by interface builder bindings.
        IBOutlet EnemyManager *enemyManager;
    }
    - (void)updateEnemies;
@end
// GameEngine.m
@implementation GameEngine
    - (void)updateEnemies
    {
        [enemyManager update];
    }
@end

If I were to test updateEnemies, I would have to detect if update was called for the enemyManager. If I just initate a GameEngine in my tests like the example below, enemyManager will not be hooked up (due to the lack of reference to the nib file which hooks up the bindings). This means that there’s no way to see what actually goes on inside the class.

One might think that this could be an issue, but it’s actually quite nice once you apply some additional test code.

Injecting the IBOutlet bound object

To setup you IBOutlets in your tests, you simply have to add an additional initializer as a category in your test code. This wont expose any additional initializer in the classes API outside of the tests, and it will allow you to replace the outlet bindings with mocks.

// GameEngineTestCases.m
@interface GameEngine (testing)
    - (id)initWithEnemyManager:(EnemyManager*)manager;
@end 

@implementation GameEngine (testing)
    - (id)initWithEnemyManager:(EnemyManager*)manager
    {
        self = [self init];
        enemyManager = manager;
        return self;
    }
@end

@implementation GameEngineTestCases
    - (void)testUpdateEnemies
    {
        id mockEnemyManager = [OCMockObject mockForClass:[EnemyManager class]];
        [[mockEnemyManager expect] update];
        GameEngine *engine = [[GameEngine alloc] initWithEnemyManager:mockEnemyManager];
        [engine updateEnemies];
        [mockEnemyManager verify];
    }
@end

Now you’re safely able to test your class without having to worry about nib files or external classes.

buffi Uninteresting

CVS is cute

June 21st, 2009

I played around a bit with Wireshark yesterday, and the initial server response when connecting to a CVS server is pretty cute.

CVS is cute

Unfortunately I’m guessing that nobody loves CVS anymore…

buffi Uninteresting