How to mock an object with OCMock which isn't passed as a parameter to method?

soguy picture soguy · Aug 29, 2013 · Viewed 7.1k times · Source

I have a method I would like to test with OCMock but not sure how to do that. I need to mock ExtClass which isn't defined as part of my code (external library):

+(NSString *)foo:(NSString *)param
{
    ExtClass *ext = [[ExtClass alloc] initWithParam:param];
    if ([ext someMethod])
        return @"A";
    else
        return @"B";
}

Thanks in advance!

Answer

e1985 picture e1985 · Aug 29, 2013

OCMock 2

id mock = [OCMockObject mockForClass:[ExtClass class]];
// We stub someMethod
BOOL returnedValue = YES;
[[[mock stub] andReturnValue:OCMOCK_VALUE(returnedValue)] someMethod];

// Here we stub the alloc class method **
[[[mock stub] andReturn:mock] alloc];
// And we stub initWithParam: passing the param we will pass to the method to test
NSString *param = @"someParam";
[[[mock stub] andReturn:mock] initWithParam:param];

// Here we call the method to test and we would do an assertion of its returned value...
[YourClassToTest foo:param];

OCMock3

// Parameter
NSURL *url = [NSURL URLWithString:@"http://testURL.com"];

// Set up the class to mock `alloc` and `init...`
id mockController = OCMClassMock([WebAuthViewController class]);
OCMStub([mockController alloc]).andReturn(mockController);
OCMStub([mockController initWithAuthenticationToken:OCMOCK_ANY authConfig:OCMOCK_ANY]).andReturn(mockController);

// Expect the method that needs to be called correctly
OCMExpect([mockController handleAuthResponseWithURL:url]);

// Call the method which does the work
[self.myClassInstance authStarted];

OCMVerifyAll(mockController);

Notes

Ensure that in both cases you stub two methods (alloc and the init... method). Also, make sure that both stubbing calls are made on the instance of the class mock (not the class itself).

Docs: Class methods section in the OCMock features

Alternatives

This (strange)solution may be useful in case you want to test legacy code that due to whatever reason you cannot refactor. However, if you can modify the code you should refactor it and get an ExtClass object as a parameter, not a string, delegating the creation of ExtClass out of that method. Your production and test code would be simpler and clearer, specially in a more complex real life case, not in this simple example.