4

How can I verify that passing block is execute correctly ?

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    [self updatePostalCode:newLocation withHandler:^(NSArray *placemarks, NSError *error) {
    // code that want to test
        CLPlacemark *placemark = [placemarks objectAtIndex:0];
        self.postalCode = [placemark postalCode];
        _geocodePending = NO;
    }];

    ....
}

I want to know that postalCode, _geocodePending is set correctly, but I can't figure out how to do this with OCMock.

Added code

id mockSelf = [OCMockObject partialMockForObject:_location];

    id mockPlacemart = (id)[OCMockObject mockForClass:[CLPlacemark class]];

    [[[mockPlacemart stub] andReturn:@"10170"] postalCode];

    [mockSelf setGeocodePending:YES];
    [mockSelf setPostalCode:@"00000"];

    [self.location handleLocationUpdate]([NSArray arrayWithObject:mockPlacemart], nil);

    STAssertFalse([mockSelf geocodePending], @"geocodePending should be FALSE");
    STAssertTrue([[mockSelf postalCode] isEqualToString:@"10170"], @"10170", @"postal is expected to be 10170 but was %@" , [mockSelf postalCode]);
sarunw
  • 8,036
  • 11
  • 48
  • 84

1 Answers1

6

Return your handler block from a method on your class. There are a few good reasons to do this, including testability.

- (void (^)(NSArray *, NSError *))handleLocationUpdate {
    __weak Foo *weakself = self;
    return ^(NSArray *placemarks, NSError *error) {
        CLPlacemark *placemark = [placemarks objectAtIndex:0];
        weakself.postalCode = [placemark postalCode];
        weakself.geocodePending = NO;
    }
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    [self updatePostalCode:newLocation withHandler:[self handleLocationUpdate]];

    ....
}

Then, in your test:

-(void)testLocationUpdates {
    id mockPlacemark = [OCMockObject mockForClass:[CLPlacemark class]];
    [[[mockPlacemark stub] andReturn:@"99999"] postalCode];

    myClass.geocodePending = YES;
    myClass.postalCode = @"00000";

    [myClass handleLocationUpdate]([NSArray arrayWithObject:mockPlacemark], nil);

    expect(myClass.geocodePending).toBeFalsy;
    expect(myClass.postalCode).toEqual(@"99999");
}
Christopher Pickslay
  • 17,523
  • 6
  • 79
  • 92
  • Thanks that work perfectly and article explain the wisdom behinds it, but I don't understand `[myClass handleLocationUpdate]([NSArray arrayWithObject:mockPlacemark], nil); ` and never see `expect().to...` could I have reference on those ? And I put my code replicate your in the question, am I understand right ? – sarunw Jun 27 '12 at 06:02
  • If you define the method as listed, then `[myClass handleLocationUpdate]` returns the block. You're executing the block as a function and passing in the placemarks array containing your mock. – Christopher Pickslay Jun 27 '12 at 06:04
  • `expect()` is from Pete Kim's excellent matcher framework, [Expecta](https://github.com/petejkim/expecta). We use it in all of our projects. – Christopher Pickslay Jun 27 '12 at 06:06
  • great example of loose coupling – Zayin Krige Jul 26 '13 at 15:00
  • 'self' needs to be weak in the method that returns your block. – William Power May 27 '15 at 22:43