Exposing Private Methods Cleanly When Testing iOS Apps

When unit testing iOS apps you will want to be able to test the internal methods of your classes in your test suite but from the outside the non-public methods won’t be readily accessible. One way around this could be to make the method public; this would allow you to test it and wouldn’t break your app but now you’ve added clutter to your interface which over time could add up to a significant problem, particularly if you (or a coworker) start to rely on that once-private method being accessible from outside the class.

An alternative solution which preserves your interfaces is to extend the interface of the class under test within the test file, thus temporarily making public the method you wish to test without affecting the original interface. So if we have a class we wish to test called CellGrid and we want to test the private method (NSInteger) cellIndexForPoint:(CGPoint)point then our test file might look like:

#import "CellGridTests.h"
#import "CellGrid.h"

@interface CellGrid(Test)

- (NSInteger)cellIndexForPoint:(CGPoint)point;

@end

@implementation CellGridTests

- (void)setUp {
    [super setUp];

    // Set-up code here.
}

- (void)tearDown {
    // Tear-down code here.

    [super tearDown];
}

- (void)testCellGridIndexCalculation {
    CellGrid *cellGrid = [[CellGrid alloc] init];
    STAssertEquals([cellGrid cellIndexForPoint(CGPointMake(0, 0))], 0, "...");
}

@end

Using this pattern, you can keep your interfaces clean and with the appropriate level of encapsulation; it will even let you add new methods to the class but I would not recommend this as it increases the risk that the tests won’t reflect the behaviour of the app in production (due to the addition of new code internal to the class being tested). That said, you can use this technique to add new methods to existing classes (such as UIColor or NSString) that you can then use in your app, and test safely.