0
votes

I have just downloaded OCMock to use for testing a library I am working on. I ran into issues in the very first stub I tried to do. I have an object that handles all network operations, and I have another object that has a map where it paints information received. I want to mock the network object to test the map object.

Here is my test file:

@interface STMap ()

@property (retain) STNetwork* router;

@end

@interface testSTMap : XCTestCase <STMapDelegate>

@property (retain) MKMapView* mapView;
@property (retain) STMap* testMap;
@property (retain) id mockRouter;

@end

@implementation testSTMap

- (void)setUp {
    [super setUp];
    // Put setup code here. This method is called before the invocation of each test method in the class.
    self.mapView = [[MKMapView alloc] init];
    self.testMap = [[STMap alloc] initWithDelegate:self andKey:@"test" andRouteOptions:nil
                                            andMap:self.mapView andOverlayLevel:MKOverlayLevelAboveLabels];
    self.mockRouter = OCMClassMock([STNetwork class]);
    self.testMap.router = self.mockRouter;
}

- (void)testExample {
    // This is an example of a functional test case.
    // Use XCTAssert and related functions to verify your tests produce the correct results.
    OCMStub([self.mockRouter getRouteForStartLat:[OCMArg any] andStartLon:[OCMArg any]
                                       andEndLat:[OCMArg any] andEndLon:[OCMArg any]])._andReturn(nil);
    [self.testMap addRouteForStartLat:@0 andStartLon:@0 andEndLat:@1 andEndLon:@1];
}

As you can see, I retain all 3 properties so the error doesn't come from the test itself. My map object has private properties, including the network object, this is why I created an extension in the test to be able to change it to the mock object. At this point, I am not even calling the map object yet, so I will not post its code.

The network object looks like this (all properties are private):

@interface STNetwork ()

@property (retain) NSString* url;
@property (weak) id<STNetworkDelegate> delegate;
@property (retain) NSString* key;
@property (copy) CompletionBlock completionHandler;

@end

I know the delegate property must be weak to avoid retain cycles, so I doubt this is causing the error.

The error is thrown in the line with OCMStub. Trace:

  • OCMStubRecorder.m- line 112: if(OCMIsObjectType([aValue objCType])). At this point, aValue is nil, this is where the problem starts, but I don't know where aValue came from.
  • OCMFunctions.m line 78: const char *unqualifiedObjCType = OCMTypeWithoutQualifiers(objCType);. Here, objCType is NULL, as expected since aValue is nil.
  • OCMFunctions.m line 40: while(strchr("rnNoORV", objCType[0]) != NULL). Here is where the bad access actually happens.

Things I tried:

  • Changing the [OCMArg any] to hard coded values
  • Returning a number, NSNull, and void from the mock

None of these got me past the bad access. Any help is greatly appreciated!

- (void)getRouteForStartLat: (NSNumber*) startLat
                andStartLon: (NSNumber*) startLon
                  andEndLat: (NSNumber*) endLat
                  andEndLon: (NSNumber*) endLon;
1
Can you provide the declaration of the getRouteForStartLat:andStartLon:andEndLat:andEndLon: method? - Erik Doernenburg
@ErikDoernenburg I added the declaration you requested - gamda

1 Answers

2
votes

Have you tried using andReturn()? The version with the underscore prefix is for internal use only.