Подтвердить что ты не робот

EXC_BAD_ACCESS при доступе к параметрам в andDo 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, но это усложняет тест.

Можно ли избежать аварии с помощью &Do?

UPDATE Хорошо, я постараюсь дать лучший пример здесь: Вот новый фрагмент тестового кода:

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

Код по-прежнему сбой при выходе из startDownloadingImagesAsync с EXC_BAD_ACCESS. Если я добавлю точку останова внутри блока andDo, я вижу, что элемент управления достигает этой точки и возвращает правильные объекты через getArgument.

Тем не менее, если я использую getArgument внутри блока, он сбрасывает все, что я пытаюсь сделать.

P.S. Спасибо за помощь.

4b9b3361

Ответ 1

У меня возникла аналогичная проблема при использовании метода 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

Я попытаюсь добавить более подробное объяснение позже.

Ответ 2

Я думаю, проблема в том, что вы пытаетесь напрямую вызвать макет. Для того, что вы пытаетесь сделать, вам не нужен макет. Просто вызовите метод и убедитесь, что изображение установлено:

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

Ответ 3

В моем случае это происходило, потому что я представил другой параметр этому методу, поэтому параметр блока был сдвинут на один.

Я исправил его, изменив [inv getArgument:&op atIndex:2] на [inv getArgument:&op atIndex:3]