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

Передача примитивов в 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 этому макету.

4b9b3361

Ответ 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);
}