How to mock ** parameter in OCMock in ARC
Asked Answered
D

2

14

One of the parameter of my method is **error and my project is in ARC mode. When writing stub for this method to call a mock method i set parameter to below possible values. Either it causes compile error or failing to match the argument to call the mock method.

  1. OCMOCK_ANY
  2. [OCMArg anyPointer]
  3. Created a NSError object error and said [OCMArg setTo:error].

Nothing works.

How to mock such method? Please advice.

Edit

-(id)init{

    self = [super init];

    if (self) {
        id wcm = [OCMockObject partialMockForObject:self];
        [[[wcm stub] andCall:@selector(mockGetWakeupCallsForRoomNumber:error:)    onObject:self] getWakeupCallsForRoomNumber:OCMOCK_ANY error:((NSError __autoreleasing **)    [OCMArg anyPointer])];
        //[[[wcm stub] andCall:@selector(testMockMethod) onObject:self] testMethod];

    }
    return self;

}

-(void)testMethod
{
    NSLog(@"Original");
}

-(void)testMockMethod
{
    NSLog(@"Mock");
}
-(NSArray*)mockGetWakeupCallsForRoomNumber:(NSString*)roomNumber error:(NSError**)error
{
    @throw @"Mock method called";
}

-(NSArray*)getWakeupCallsForRoomNumber:(NSString*)roomNumber error:(NSError**)error
{
}

Mock on testMockMethod works.

Donelladonelle answered 17/8, 2013 at 5:31 Comment(0)
P
20

You can use setTo: or anyPointer if you cast it:

[[[mockFoo expect] andReturn:nil] someMethodWithError:((NSError __autoreleasing **)[OCMArg anyPointer])];

or:

NSError *error;
[[[mockFoo expect] andReturn:nil] someMethodWithError:((NSError __autoreleasing **)[OCMArg setTo:error])];

Here's a test case that passes with the method you've posted:

@interface Foo : NSObject{}

-(NSArray*)mockGetWakeupCallsForRoomNumber:(NSString*)roomNumber error:(NSError**)error;
-(NSArray*)getWakeupCallsForRoomNumber:(NSString*)roomNumber error:(NSError**)error;

@end

@implementation Foo

-(NSArray*)mockGetWakeupCallsForRoomNumber:(NSString*)roomNumber error:(NSError**)error
{
    return @[@"bar"];
}

-(NSArray*)getWakeupCallsForRoomNumber:(NSString*)roomNumber error:(NSError**)error
{
    return nil;
}

@end

@interface SomeTest : SenTestCase {}
@end

@implementation SomeTest

-(void)testMethod
{
    Foo *foo = [Foo new];
    id wcm = [OCMockObject partialMockForObject:foo];
    [[[wcm stub] andCall:@selector(mockGetWakeupCallsForRoomNumber:error:) onObject:foo] getWakeupCallsForRoomNumber:OCMOCK_ANY error:((id __autoreleasing *)[OCMArg anyPointer])];
    NSError *error;
    NSArray *calls = [wcm getWakeupCallsForRoomNumber:@"foo" error:&error];
    STAssertEquals(calls[0], @"bar", @"should match");
}

@end
Pedestrian answered 17/8, 2013 at 16:29 Comment(10)
[[[wcm stub] andCall:@selector(mockGetWakeupCallsForRoomNumber:error:) onObject:self] getWakeupCallsForRoomNumber:OCMOCK_ANY error:((NSError __autoreleasing **)[OCMArg anyPointer])]; calls are not invoking the mock method. Please help.Donelladonelle
Please add the test case and the code you're testing to your question.Pedestrian
Added the code. This is in library responsible to connect backend server. My intention is to have the ability to provide mock until required service is implemented at the backend.Donelladonelle
Is this something we could improve in OCMock? Is there any way to declare the anyPointer/setTo: methods so that the cast isn't needed?Afghani
@Galaxy see my updated answer, with a passing test case. In the code you added, it looks like you're trying to create a partial mock of a test class, which I would not expect to behave well. But with a real-world case of mocking another class, it works fine.Pedestrian
@ErikDoernenburg the test case works with the more generic cast (id __autoreleasing *). Perhaps anyPointer could be updated to return (id __autoreleasing *), though I'm not sure if there might be other side effects.Pedestrian
@ChristopherPickslay, thanks. With a minor improvement it is working good. I added another parameter in the mean time:) [[[wcm stub] andCall:@selector(getWakeupCallsForRoomNumber:error:serverMessage:) onObject:self] getWakeupCallsForRoomNumber:OCMOCK_ANY error:((NSError *__autoreleasing *)[OCMArg anyPointer]) serverMessage:((NSString *__autoreleasing *)[OCMArg anyPointer])];Donelladonelle
@ChristopherPickslay Don't want to make this too off-topic. That said, I think an additional method on OCMArg could help, e.g. anyObjectRef. We could leave anyPointer as generic as it is and for cases like this provide a differently typed version.Afghani
@ChristopherPickslay, take a look at my answer. It doesn't have to be id. Can be more specific like (NSString *__autoreleasing *)[OCMArg anyPointer]Donelladonelle
This is ancient now, but OCMock now has [OCMArg anyObjectRef] that should allow you to avoid the cast and reads a little better.Philosophize
D
2

Finally this is what i implemented.

[
 [[wcm stub] 
           andCall:@selector(getWakeupCallsForRoomNumber:error:serverMessage:) 
   onObject:self]
   getWakeupCallsForRoomNumber:OCMOCK_ANY
                         error:((NSError *__autoreleasing *)[OCMArg anyPointer]) 
                 serverMessage:((NSString *__autoreleasing *)[OCMArg anyPointer])];
Donelladonelle answered 23/8, 2013 at 9:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.