I often find in my iPhone Objective-C unit tests that I want stub out a class method, e.g. NSUrlConnection's +sendSynchronousRequest:returningResponse:error: method.
Simplified example:
- (void)testClassMock
{
id mock = [OCMockObject mockForClass:[NSURLConnection class]];
[[[mock stub] andReturn:nil] sendSynchronousRequest:nil returningResponse:nil error:nil];
}
When running this, I get:
Test Case '-[WorklistTest testClassMock]' started.
Unknown.m:0: error: -[WorklistTest testClassMock] : *** -[NSProxy doesNotRecognizeSelector:sendSynchronousRequest:returningResponse:error:] called!
Test Case '-[WorklistTest testClassMock]' failed (0.000 seconds).
I've had a really hard time finding any documentation on this, but I assume that class methods aren't supported by OCMock.
I found this tip after a lot of Googling. It works, but is very cumbersome: http://thom.org.uk/2009/05/09/mocking-class-methods-in-objective-c/
Is there anyway to do this within OCMock? Or can someone think of a clever OCMock category object that could be written to accomplish this sort of thing?
Update for OCMock 3
OCMock has modernized its syntax for supporting class method stubbing:
id classMock = OCMClassMock([SomeClass class]);
OCMStub(ClassMethod([classMock aMethod])).andReturn(aValue);
Update
OCMock now supports class method stubbing out of the box. The OP's code should now work as posted. If there is an instance method with the same name as the class method, the syntax is:
[[[[mock stub] classMethod] andReturn:aValue] aMethod]
See OCMock's Features.
Original Answer
Sample code following Barry Wark's answer.
The fake class, just stubbing connectionWithRequest:delegate:
@interface FakeNSURLConnection : NSURLConnection
+ (id)sharedInstance;
+ (void)setSharedInstance:(id)sharedInstance;
+ (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate;
- (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate;
@end
@implementation FakeNSURLConnection
static id _sharedInstance;
+ (id)sharedInstance { if (!_sharedInstance) { _sharedInstance = [self init]; } return _sharedInstance; }
+ (void)setSharedInstance:(id)sharedInstance { _sharedInstance = sharedInstance; }
+ (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate {
return [FakeNSURLConnection.sharedInstance connectionWithRequest:request delegate:delegate];
}
- (NSURLConnection *)connectionWithRequest:(NSURLRequest *)request delegate:(id<NSURLConnectionDelegate>)delegate { return nil; }
@end
Switching to and from the mock:
{
...
// Create the mock and swap it in
id nsurlConnectionMock = [OCMockObject niceMockForClass:FakeNSURLConnection.class];
[FakeNSURLConnection setSharedInstance:nsurlConnectionMock];
Method urlOriginalMethod = class_getClassMethod(NSURLConnection.class, @selector(connectionWithRequest:delegate:));
Method urlNewMethod = class_getClassMethod(FakeNSURLConnection.class, @selector(connectionWithRequest:delegate:));
method_exchangeImplementations(urlOriginalMethod, urlNewMethod);
[[nsurlConnectionMock expect] connectionWithRequest:OCMOCK_ANY delegate:OCMOCK_ANY];
...
// Make the call which will do the connectionWithRequest:delegate call
...
// Verify
[nsurlConnectionMock verify];
// Unmock
method_exchangeImplementations(urlNewMethod, urlOriginalMethod);
}