访问OCMock的andDo中的参数时的EXC_BAD_ACCESS

时间:2012-11-07 11:10:03

标签: iphone ios xcode ocunit ocmock

我正在尝试使用OCMock的stub 和Do 方法编写一段代码。

在这种情况下,正在测试UIImageView扩展类。我想检查扩展名是否使用非零参数调用 [self setImage:] (稍后将使用其他图像比较)。

使用OCMock的和Do 方法时,在块完成后,测试会与EXC_BAD_ACCESS崩溃。

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];

我现在找到的唯一解决方案是使用 andCall 而不是 andDo ,但这会使测试变得复杂。

我可以使用 andDo 来避免崩溃吗?

更新 好吧,我会尝试在这里给出一个更好的例子: 这是测试代码的新部分:

- (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");
}

以下是经过测试的代码(来自PInfo的单一方法):

- (void)startDownloadingImagesAsync:(bool)isThumbnailImg
{
    NSInvocationOperation *inv;

    inv = [[NSInvocationOperation alloc] initWithTarget:self
                                     selector:@selector(loadThumbnailWorker:)
                                       object:nil];
    [[self downloadQueue] addOperation:inv];
}

使用EXC_BAD_ACCESS退出 startDownloadingImagesAsync 时代码仍然崩溃。 如果我在 andDo 块中添加断点,我会看到控件到达此点并通过 getArgument 检索正确的对象。

然而,如果我在块中使用 getArgument ,它会崩溃我试图做的事情。

P.S。谢谢你的帮助。

3 个答案:

答案 0 :(得分:29)

使用NSProxy的forwardInvocation:方法时遇到了类似的问题。

你可以试试下面的吗?

NSInvocationOperation *op; // Change this line
__unsafe_unretained NSInvocationOperation *op; // to this line

或者另一种方法可能是保留NSInvocation的参数:

[invocation retainArguments];

http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSInvocation_Class/Reference/Reference.html#//apple_ref/occ/instm/NSInvocation/retainArguments

我稍后会尝试添加更详细的解释。

答案 1 :(得分:0)

我认为问题在于您正在尝试直接调用模拟对象。对于你想要做的事情,你不应该需要一个模拟对象。只需调用该方法并验证图像已设置:

expect(myObject.imageView.image).to.beNil();
[myObject do_something_that_calls_setImage];
expect(myObject.imageView.image).not.to.beNil();

如果您真的想出于某种原因使用模拟,可以使用真实的UIImageView和部分模拟来实现:

UIImageView *imageView = myObject.imageView;
id mockView = [OCMockObject partialMockForObject:imageView];
__block BOOL imageSet = NO;
[[[mockView stub] andDo:^(NSInvocation *invocation) {
      UIImage *img;
      [invocation getArgument:&img atIndex:2];
      imageSet = (img != nil);
  }] setImage:OCMOCK_ANY];

[myObject do_something_that_calls_setImage];
expect(imageSet).to.beTruthy();

答案 2 :(得分:0)

在我的情况下,这种情况正在发生,因为我向此方法引入了另一个参数,因此块参数被移动了一个。

我通过将[inv getArgument:&op atIndex:2]更改为[inv getArgument:&op atIndex:3]

来修复此问题