I’ve seen quite a few questions posted on various programming websites asking about how to access a class’s private methods from a unit test file. In Objective-C there are several ways to do this:
- Use performSelector in the unit test to access private methods
- Use Key-Value Coding to access properties and iVars.
- Define the methods and properties in a class extension.
I really like the 3rd approach the best. In case you aren’t familiar with class extensions in Objective-C, these are also known as anonymous categories. The following example shows a simple class definition and implementation that includes a class extension:
// ExampleClass.h
// Public header file
#import <Foundation/Foundation.h>
@interface ExampleClass : NSObject {
}
// Publicly exposed methods
- (void)exampleMethod;
// Publicly exposed properties
@property (nonatomic, retain) NSString * exampleProperty;
@end
Â
// ExampleClass.m
// ExampleClass implementation
#import "ExampleClass.h" Â
@implementation ExampleClass
// Properties
@synthesize exampleProperty = exampleProperty_;
// Methods
- (int)exampleMethod { Â
// Do whatever this method needs to do...
return 3;
}Â
In this example, I’ve defined a class with an exampleMethod and exampleProperty. Both of these are declared publicly and so can be accessed easily from a unit test file as shown below:
// ExampleClass.m
// ExampleClass unit tests
#import <SenTestingKit/SenTestingKit.h>
#import "ExampleClass.h"
@interface ExampleClassTests : SenTestCase {
}
@property (nonatomic, retain) ExampleClass * testClass;
@end
@implementation ExampleClassTests
@synthesize testClass = testClass_;
-(void)testExampleMethodReturnValue {
ExampleClass * ourClass = [[ExampleClass alloc]init];
int returnValue = [ourClass exampleMethod];
STAssertTrue( returnValue == 3,
@"Return value should have been 3 but was %d",returnValue);
}
@end
Now, what if you wanted to keep exampleMethod private, and not expose it in the public ExampleClass.h header file? As mentioned above, the unit test could use performSelector to execute the method, but this approach is limited to passing a single argument and precludes letting the compiler check your parameters, etc.
A better way is to declare exampleMethod in a class extension, and put that class extension in a separate header file. This file can then be imported into both the ExampleClass implementation file as well as any unit test files that need to access exampleMethod. This class extension header file is shown below.
// ExampleClassExtension.h
// ExampleClass Class Extension
// Declare any private methods and properties in this file.
// #import this file into the implementation file and unit test files.
@interface ExampleClass ()
- (int)exampleMethod;
@end
It’s just that simple. Using a class extension is considered a best practice for declaring your private methods and properties. Doing this makes it clear that these methods and properties are not intended for public use, otherwise they would be listed in the main header. But putting them in a class extension in a separate header file allows your unit tests to use them also. These methods and properties can be easily refactored, and will work correctly with autocompletion.