15

OCMock のスタブandDoメソッドを使用してコード ブロックを記述しようとしています。

この場合、UIImageView 拡張クラスがテストされています。拡張機能が非 nil のパラメーターで[self setImage:]を呼び出すことを確認したい(後で他の画像比較が使用されます)。

OCMock のandDoメソッドを使用すると、ブロックの完了後に 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];

私が今のところ見つけた唯一の解決策は、 andDo の代わりに andCall を使用することですこれはテストを複雑にします。

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を使用すると、何をしようとしてもクラッシュします。

PS助けてくれてありがとう。

4

3 に答える 3

33

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

詳しい説明は後ほど追記しようと思います。

于 2012-12-12T00:56:47.277 に答える
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();
于 2012-11-26T05:42:21.690 に答える