Question

I'm learning how to use OCMock to test my iPhone's project and I have this scenario: a HeightMap class with a getHeightAtX:andY: method, and a Render class using HeightMap. I'm trying to unit test Render using some HeightMap mocks. This works:

id mock = [OCMockObject mockForClass:[Chunk class]];
int h = 0;
[[[mock stub] andReturnValue:OCMOCK_VALUE(h)] getHeightAtX:0 andY:0];

Of course, works only for x=0 and y=0. I want to test using a "flat" height map. This means I need to do something like this:

id chunk = [OCMockObject mockForClass:[Chunk class]];
int h = 0;
[[[chunk stub] andReturnValue:OCMOCK_VALUE(h)] getHeightAtX:[OCMArg any] andY:[OCMArg any]];

But this raises two compilation warnings:

warning: passing argument 1 of 'getHeightAtX:andY:' makes integer from pointer without a cast

and a runtime error:

unexpected method invoked: 'getHeightAtX:0 andY:0 stubbed: getHeightAtX:15545040 andY:15545024'

What am I missing? I found no way to pass a anyValue to this mock.

Was it helpful?

Solution

OCMock doesn't currently support loose matching of primitive arguments. There's a discussion about potential changes to support this on the OCMock forums, though it seems to have stalled.

The only solution I've found is to structure my tests in such a way that I know the primitive values that will be passed in, though it's far from ideal.

OTHER TIPS

It's been awhile since this question has been asked but I ran into this issue myself and couldn't find a solution anywhere. OCMock now supports ignoringNonObjectArgs so an example of an expect would be

[[[mockObject expect] ignoringNonObjectArgs] someMethodWithPrimitiveArgument:5];

the 5 doesn't actually do anything, just a filler value

Use OCMockito instead.

It supports primitive argument matching.

For instance, in your case:

id chunk = mock([Chunk class]);
[[given([chunk getHeightAtX:0]) withMatcher:anything() forArgument:0] willReturnInt:0];

In addition to Andrew Park answer you could make it a little bit more general and nice looking:

#define OCMStubIgnoringNonObjectArgs(invocation) \
({ \
    _OCMSilenceWarnings( \
        [OCMMacroState beginStubMacro]; \
        [[[OCMMacroState globalState] recorder] ignoringNonObjectArgs]; \
        invocation; \
        [OCMMacroState endStubMacro]; \
    ); \
})

The you can use it like that:

OCMStubIgnoringNonObjectArgs(someMethodParam:0 param2:0).andDo(someBlock)

You can do the same for expecting. This case is for stubbing as topic starter request. It was tested with OCMock 3.1.1.

Despite being fairly hacky, the approach of using expectations to store the passed block to call later in the test code has worked for me:

- (void)testVerifyPrimitiveBlockArgument
{
    // mock object that would call the block in production
    id mockOtherObject = OCMClassMock([OtherObject class]);

    // pass the block calling object to the test object
    Object *objectUnderTest = [[Object new] initWithOtherObject:mockOtherObject];

    // store the block when the method is called to use later
    __block void (^completionBlock)(NSUInteger value) = nil;
    OCMExpect([mockOtherObject doSomethingWithCompletion:[OCMArg checkWithBlock:^BOOL(id value) { completionBlock = value; return YES; }]]);

    // call the method that's being tested
    [objectUnderTest doThingThatCallsBlockOnOtherObject];

    // once the expected method has been called from `doThingThatCallsBlockOnOtherObject`, continue
    OCMVerifyAllWithDelay(mockOtherObject, 0.5);
    // simulate callback from mockOtherObject with primitive value, can be done on the main or background queue
    completionBlock(45);
}

You could do like this: id chunk = OCMClassMock([Chunk class]) OCMStub([chunk ignoringNonObjectArgs] getHeightAtX:0 andY:0]])

Readmore at: http://ocmock.org/reference/#argument-constraints

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top