I am trying to write an block of code using OCMock's stub andDo method.
In this case UIImageView extension class is being tested. I want to check that the extension calls [self setImage:] with parameter that is non-nil (later other image comparison will be used).
When using OCMock's andDo method, the test crashes with EXC_BAD_ACCESS after the block completes.
id mockView = [OCMockObject mockForClass:[UIImageView class]];
[[[mockView stub] andDo:^(NSInvocation *invocation)
{
UIImage *img;
[invocation getArgument:&img atIndex:2]; <---- line causing the exception
somebodySetImage |= (img != nil);
}] setImage:OCMOCK_ANY];
[mockView do_something_that_calls_setImage];
The only solution that I've found for now is using andCall instead of andDo, but this complicates the test.
Can I avoid the crash with andDo?
UPDATE Well, I will try to give a better example here: Here is the new piece of the test code:
- (void)testDownloadingThumbnail
{
PInfo *_sut = [[PInfo alloc] init];
__block id target = nil;
id mock = [OCMockObject mockForClass:[NSOperationQueue class]];
[[[mock expect] andDo:^(NSInvocation *inv)
{
NSInvocationOperation *op;
[inv getArgument:&op atIndex:2];
target = [[op invocation] target]; /* replacing this line with STAssert does not help either */
}] addOperation:OCMOCK_ANY];
[_sut setDownloadQueue:mock];
[_sut startDownloadingImagesAsync:YES];
[mock verify];
STAssertEqualObjects(target, _sut, @"invalid op target");
}
Here is the tested code (single method from PInfo):
- (void)startDownloadingImagesAsync:(bool)isThumbnailImg
{
NSInvocationOperation *inv;
inv = [[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(loadThumbnailWorker:)
object:nil];
[[self downloadQueue] addOperation:inv];
}
The code still crashes upon exit from startDownloadingImagesAsync with EXC_BAD_ACCESS. If I add a breakpoint inside the andDo block, I see that the control reaches this point and retrieves correct objects via getArgument.
Yet, if I use getArgument inside the block, it crashes whatever I try to do.
P.S. Thanks for help.
I ran into a similar problem when using NSProxy's forwardInvocation: method.
Can you try the below?
NSInvocationOperation *op; // Change this line
__unsafe_unretained NSInvocationOperation *op; // to this line
Or another approach could be to retain NSInvocation's arguments:
[invocation retainArguments];
http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSInvocation_Class/Reference/Reference.html#//apple_ref/occ/instm/NSInvocation/retainArguments
I'll try to add a more detailed explanation later.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With