Передача примитивов в OCMock-заглушку
Я изучаю, как использовать OCMock для тестирования моего проекта iPhone, и у меня есть этот сценарий: класс HeightMap с методом getHeightAtX:andY:
и класс Render с использованием HeightMap
. Я пытаюсь unit test Render использовать некоторые HeightMap
mocks. Это работает:
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]];
Но это вызывает два предупреждения о компиляции:
предупреждение: передача аргумента 1 из 'getHeightAtX:andY:'
делает целое число из указателя без литой
и ошибка времени выполнения:
вызванный неожиданный метод: 'getHeightAtX:0 andY:0 stubbed: getHeightAtX:15545040 andY:15545024'
Что мне не хватает? Я не нашел способа передать anyValue
этому макету.
Ответы
Ответ 1
OCMock в настоящее время не поддерживает свободное сопоставление примитивных аргументов. Там обсуждаются потенциальные изменения для поддержки этого на форумах OCMock, хотя он, похоже, застопорился.
Единственное решение, которое я нашел, - это структурировать мои тесты таким образом, чтобы я знал примитивные значения, которые будут переданы, хотя это далеко не идеально.
Ответ 2
Прошло некоторое время с тех пор, как этот вопрос был задан, но я столкнулся с этим вопросом и не смог найти решение нигде. OCMock теперь поддерживает ignoringNonObjectArgs
, поэтому пример expect
будет
[[[mockObject expect] ignoringNonObjectArgs] someMethodWithPrimitiveArgument:5];
5 фактически ничего не делает, просто значение наполнителя
Ответ 3
Используйте OCMockito вместо этого.
Он поддерживает примитивное сопоставление аргументов.
Например, в вашем случае:
id chunk = mock([Chunk class]);
[[given([chunk getHeightAtX:0]) withMatcher:anything() forArgument:0] willReturnInt:0];
Ответ 4
В дополнение к Andrew Park ответ вы можете сделать его немного более общим и приятным:
#define OCMStubIgnoringNonObjectArgs(invocation) \
({ \
_OCMSilenceWarnings( \
[OCMMacroState beginStubMacro]; \
[[[OCMMacroState globalState] recorder] ignoringNonObjectArgs]; \
invocation; \
[OCMMacroState endStubMacro]; \
); \
})
Вы можете использовать его так:
OCMStubIgnoringNonObjectArgs(someMethodParam:0 param2:0).andDo(someBlock)
Вы можете сделать то же самое для ожидания. Этот случай предназначен для обрезания в качестве запроса стартера темы. Он был протестирован с OCMock 3.1.1.
Ответ 5
Несмотря на то, что он довольно хаки, подход использования ожиданий для хранения переданного блока для вызова позже в тестовом коде работал у меня:
- (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 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);
}