OCMockを使用してiPhoneのプロジェクトをテストする方法を学んでおり、次のシナリオがあります。getHeightAtX:andY:
メソッドを使用するHeightMapクラスと、HeightMap
を使用するRenderクラスです。いくつかのHeightMap
モックを使用してRenderのユニットテストを試みています。これは機能します:
id mock = [OCMockObject mockForClass:[Chunk class]];
int h = 0;
[[[mock stub] andReturnValue:OCMOCK_VALUE(h)] getHeightAtX:0 andY:0];
もちろん、x=0
とy=0
でのみ機能します。 「フラット」な高さマップを使用してテストしたいと思います。これは私がこのようなことをする必要があることを意味します:
id chunk = [OCMockObject mockForClass:[Chunk class]];
int h = 0;
[[[chunk stub] andReturnValue:OCMOCK_VALUE(h)] getHeightAtX:[OCMArg any] andY:[OCMArg any]];
しかし、これにより2つのコンパイル警告が発生します。
警告:
'getHeightAtX:andY:'
の引数1を渡すと、キャストなしでポインタから整数になります
およびランタイムエラー:
予期しないメソッドが呼び出されました:
'getHeightAtX:0 andY:0 stubbed: getHeightAtX:15545040 andY:15545024'
何が足りないのですか?このモックにanyValue
を渡す方法が見つかりませんでした。
OCMockは現在、プリミティブ引数の緩いマッチングをサポートしていません。これをサポートするための潜在的な変更についての議論があります OCMockフォーラムで 、それは行き詰まっているようですが。
私が見つけた唯一の解決策は、渡されるプリミティブ値がわかるようにテストを構造化することですが、理想からはほど遠いです。
この質問が出されてからしばらく経ちましたが、私は自分でこの問題に遭遇し、どこにも解決策を見つけることができませんでした。 OCMockはignoringNonObjectArgs
をサポートするようになったため、expect
の例は次のようになります。
[[[mockObject expect] ignoringNonObjectArgs] someMethodWithPrimitiveArgument:5];
5は実際には何もしません。フィラー値だけです。
代わりに OCMockito を使用してください。
プリミティブ引数マッチングをサポートします。
たとえば、あなたの場合:
id chunk = mock([Chunk class]);
[[given([chunk getHeightAtX:0]) withMatcher:anything() forArgument:0] willReturnInt:0];
Andrew Parkの回答に加えて、もう少し一般的で見栄えの良いものにすることができます。
#define OCMStubIgnoringNonObjectArgs(invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginStubMacro]; \
[[[OCMMacroState globalState] recorder] ignoringNonObjectArgs]; \
invocation; \
[OCMMacroState endStubMacro]; \
); \
})
あなたはそれをそのように使うことができます:
OCMStubIgnoringNonObjectArgs(someMethodParam:0 param2:0).andDo(someBlock)
あなたは期待するために同じことをすることができます。このケースは、トピックスターターリクエストとしてスタブするためのものです。 OCMock3.1.1でテストされました。
あなたはこのようにすることができます:id chunk = OCMClassMock([Chunk class]) OCMStub([chunk ignoringNonObjectArgs] getHeightAtX:0 andY:0]])
かなりハッキーですが、期待値を使用して渡されたブロックを格納し、後でテストコードで呼び出すというアプローチは、私にとってはうまくいきました。
- (void)testVerifyPrimitiveBlockArgument
{
// mock object that would call the block in production
id mockOtherObject = OCMClassMock([OtherObject class]);
// pass the block calling object to the test object
Object *objectUnderTest = [[Object new] initWithOtherObject:mockOtherObject];
// store the block when the method is called to use later
__block void (^completionBlock)(NSUInteger value) = nil;
OCMExpect([mockOtherObject doSomethingWithCompletion:[OCMArg checkWithBlock:^BOOL(id value) { completionBlock = value; return YES; }]]);
// call the method that's being tested
[objectUnderTest doThingThatCallsBlockOnOtherObject];
// once the expected method has been called from `doThingThatCallsBlockOnOtherObject`, continue
OCMVerifyAllWithDelay(mockOtherObject, 0.5);
// simulate callback from mockOtherObject with primitive value, can be done on the main or background queue
completionBlock(45);
}