Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cannot stub class method with OCMock 2.1+ in Xcode 5.0

I know that OCMock version 2.1+ supports stubbing class methods out of the box. But for some reason it's not working with me. To make sure I isolated the problem, I simply cloned the example OCMock project (which is clearly marked as version 2.2.1) and simply added this inside testMasterViewControllerDeletesItemsFromTableView:

id detailViewMock = [OCMockObject mockForClass:[DetailViewController class]];
[[[detailViewMock stub] andReturn:@"hello"] helloWorld]; 

in DetailViewController.h I added:

+ (NSString *)helloWorld;

and DetailViewController.m:

+ (NSString *)helloWorld {
    return @"hello world";
}

But I keep on getting the error:

*** -[NSProxy doesNotRecognize Selector:helloWorld] called!

to see a demo of the problem please clone this repo to see what's going on.

like image 343
abbood Avatar asked Feb 16 '23 01:02

abbood


1 Answers

That should work just fine. I just tried in a project of mine which uses XCTest on Xcode5, and that code passed.

I would 1) make sure you are using the latest version of OCMock (which is 2.2.1 right now; I think there are some fixes for both class methods and Xcode5 in the newer versions), and 2) make sure your DetailViewController class is linked in the runtime (i.e. part of the correct target) correctly.

In looking at your project, your DetailViewController class is part of both the main application, and the test target. With Xcode5, it appears this means that two copies of the class get compiled and are present in the runtime, with code in the app calling one copy, and code in the test case calling the other. This used to be a linker error (duplicate symbols), but for better or worse, the linker now appears to silently allow two copies of the same class (with the same name) to exist in the ObjC runtime. OCMock, using dynamic lookup, finds the first one (the one compiled into the app), but the test case is directly linked to the second copy (the one compiled into the test bundle). So... OCMock is not actually mocking the class you think it is.

You can see this, just for grins, by verifying as part of the test case that [DetailViewController class] will not equal NSClassFromString(@"DetailViewController") (the first is directly linked, the second is dynamic).

To fix this properly, in the "Target Memberships" for DetailViewController.m, just uncheck the test target. This way there is only one copy of the class in the runtime, and things work like you'd expect. The test bundle gets loaded into the main application, so all of the main application's classes should be available to the bundle without having to directly compile them into the bundle. Classes should only be part of one of the two targets, not both (this has always been the case).

like image 97
Carl Lindberg Avatar answered Feb 23 '23 22:02

Carl Lindberg