Question

I am using OCHamcrest and OCMockito for unit testing an iOS project. I'm working my way toward understanding using custom matchers in a mock verification of a method expectation that takes parameters. In particular, I want to verify one of the parameters passed to the expected method, but I need to pull apart the passed parameter in order to do so. To demonstrate, here's what I have for the relevant part of my test method:

EAAccessory *accessory = mock([EAAccessory class]);
UIViewController <ZKSearchManagerDelegate> *mockController = 
    mockObjectAndProtocol(
        [UIViewController class], @protocol(ZKSearchManagerDelegate)
    );
[verify(mockController) 
        zkZSensorFound:isSensorWithAccessory(accessory) 
     fromSearchManager:_sm];

The -zkZSensorFound:fromSearchManager: takes an object that contains an EAAccessory as a property. In my custom matcher, I need to open up that object and examine that property to make sure it's the same object as accessory within this test method. It's easy enough to pass accessory into the matcher as I'm doing here, but how do I get at the actual object that is passed to the parameter for use inside of my matcher? Or, am I going about this all wrong?

Update

I've accepted the answer from @JonReid, as it's a much more elegant solution than what I've concocted. Nevertheless, I think what I was using (thanks to this blog post), could be useful elsewhere:

#import "ArgumentCaptor.h"

EAAccessory *accessory = mock([EAAccessory class]);
UIViewController <ZKSearchManagerDelegate> *mockController = 
    mockObjectAndProtocol(
        [UIViewController class], @protocol(ZKSearchManagerDelegate)
    );
ArgumentCaptor *captor = argCaptor();
[verify(_mockController) zkZSensorFound:(ZKZSensor *)captor 
                      fromSearchManager:is(sameInstance(_sm))];
ZKZSensor *sensor = captor.argument;
assertThat(sensor.accessory, is(_mockAccessory));
Was it helpful?

Solution

First, be aware that when you don't specify a matcher to OCMockito, it uses the isEqual matcher. This probably isn't what you want to verify for the second argument. I'd check for identity instead:

fromSearchManager:sameInstance(_sm)

If the argument is typed, it will complain that the matcher sameInstance isn't a search manager. The workaround is to cast the matcher to remove the typing:

fromSearchManager:(id)sameInstance(_sm)

Now for the first argument: you may not need to make a custom matcher at all. If all you want to do is check a property value, use hasProperty. Assuming the property is named accessory,

zkZSensorFound:(id)hasProperty(@"accessory", sameInstance(accessory))

Putting it all together:

[verify(mockController) 
        zkZSensorFound:(id)hasProperty(@"accessory", sameInstance(accessory)) 
     fromSearchManager:(id)sameInstance(_sm)];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top