2

Intro:

I have the tool (Xcode 5.0.2), library (OCMock 2.2.1) and test (XCTest) setup mentioned in the title of this question.

Category:

I have a category on NSObject with a class method like this:

+ (BOOL) hasDeclaredPropertyWithName: (NSString*) property;

Issue with OCMock and XCTest framework?:

Now i have a simple test set-up where I'm mocking a simple value object, like this: (mocking value objects is a test smell, I know. but this is just for illustration purpose.)

- (void) testFoo {
     id mock = [OCMockObject mockForClass: [TestObject class]];
     [[[mock stub] andReturn: NO] hasDeclaredPropertyWithName: @"propertyX"];
     [mock hasDeclaredPropertyWithName: @"propertyX"];
}

When the 3rd line of the test method testFoo is executed, I end up with the error:

-[NSProxy doesNotRecognizeSelector:hasDeclaredPropertyWithName:] called!

Question:

Why mocking a class method seems impossible with OCMock (At least with my setup)?

If i make hasDeclaredPropertyWithName to an instace method like

- (BOOL) hasDeclaredPropertyWithName: (NSString*) property;

everything's working just fine!

Can someone explain this OCMock deficiency to me? Or do I have a major missconception regarding Objective-C here? :)

Is the category maybe causing headaches to the runtime and/or OCMock? Btw., I didn't try this test with a class method inside TestObject directly!

Nenad M
  • 3,055
  • 20
  • 26
  • 1
    One issue is that you can't return a primitive with andReturn. Do this: `andReturnValue:OCMOCK_VALUE((BOOL){NO})` See if that helps. – Ben Flynn Nov 20 '13 at 22:16
  • Ben, thanks for the additional info - good to know. Unfortunately it didn't help. – Nenad M Nov 21 '13 at 10:07

1 Answers1

2

Ok, so first the base case, where we are mocking a category class method:

Some class I had laying around for testing, Huzzah:

@interface Huzzah : NSObject
+ (void)doClass;
+ (void)doClass2;
+ (void)doClass3;
- (void)doInstance;
@end

I created a category:

@interface Huzzah (Cat)
+ (BOOL)hasX:(NSString *)x;
@end

@implementation Huzzah (Cat)
+ (BOOL)hasX:(NSString *)x
{
    return YES;
}
@end

In my test:

#import "Huzzah+Cat.h"
- (void)testHuzzahCat
{
    id mock = [OCMockObject mockForClass:Huzzah.class];
    [[[mock stub] andReturnValue:OCMOCK_VALUE((BOOL){NO})] hasX:OCMOCK_ANY];
    NSLog(@"hasX: %i", [Huzzah hasX:@"DoYouHas?"]);
}

Output: hasX: 0

Now let's try a category on NSObject:

@interface NSObject (Cat)
+ (BOOL)hasY:(NSString *)y;
@end

@implementation NSObject (Cat)
+ (BOOL)hasY:(NSString *)y
{
    return YES;
}
@end

Our new test:

#import "NSObject+Cat.h"
- (void)testObjectCat
{
    id mock = [OCMockObject mockForClass:Huzzah.class];
    [[[mock stub] andReturnValue:OCMOCK_VALUE((BOOL){NO})] hasY:OCMOCK_ANY];
    NSLog(@"hasY: %i", [Huzzah hasY:@"DoYouHas?"]);
}

Results in: hasY: 0

Is it possible that the implementation of category is not getting compiled into your project?

Ben Flynn
  • 18,524
  • 20
  • 97
  • 142
  • Note that I am using SenTestingKit, not XCTest. Not sure if that makes a difference. – Ben Flynn Nov 21 '13 at 17:17
  • Ok, your example helped me understand what I'm actually trying to do. Thank's for that! Can you explain me just one thing? Why can't i do the following: `NSLog(@"hasX: %i", [[mock stub] hasX:@"DoYouHas?"]);` or `NSLog(@"hasY: %i", [Huzzah hasY:@"DoYouHas?"]);`? That's actually what I've tried to illustrate via my example `[mock hasDeclaredPropertyWithName: @"propertyX"];`. OCMockObject derives from NSProxy, probably that's the reason. – Nenad M Nov 25 '13 at 08:05
  • @NenadM `[[mock stub] hasX:@"DoYouHas?"]` will return an anonymous object of type OCMockObject that will return nil (because you did not declare a return value). The object will immediately go out of scope after the `NSLog` and the class method will no longer be mocked. Your second example is what I used in my answer... is that a typo? We call the class method after we stub it. We could call it without stubbing it and it would return 1. – Ben Flynn Nov 25 '13 at 15:21
  • Sorry, this one was a typo! what i meant is: `NSLog(@"hasY: %i", [[mock stub] hasY:@"DoYouHas?"]);` regarding your example. – Nenad M Nov 25 '13 at 19:10
  • Ok, so as long as you understand that the methods you are suggesting return OCMockObjects and not integers, I think you are good to go. Review the OCMock features page: http://ocmock.org/features/ – Ben Flynn Nov 25 '13 at 20:17