Почему мое ожидание OCMock andDo вызывается только в первый раз

Я издевался над доступом к бэкэнду Parse Data и столкнулся с проблемами с OCMock.

Основной механизм доступа к данным для доступа к бэкэнду через анализPFQuery объект, построенный с [PFQuery queryWithClassName:@"ClassName"], Естественно, это хороший выбор в качестве тестового шва.

Я хочу использовать частичные макеты в этом случае по причинам, которые я не буду рассматривать в этом посте.

Вместо того, чтобы возвращать реальный PFQuery, я могу настроить этот метод класса так, чтобы он возвращал фиктивный объект следующим образом:

id queryMock = OCMClassMock([PFQuery class]);
OCMStub([queryMock queryWithClassName:className]).andDo(^(NSInvocation *invocation){
    id mockQuery = OC...;
    NSLog(@">> CREATED MOCK QUERY: %@, %p", mockQuery, &mockQuery);
    [invocation setReturnValue:&mockQuery];
});

... с фиктивным запросом, настроенным так:

PFQuery *query = [[PFQuery alloc] initWithClassName:className];

id mockQuery = OCMPartialMock(query);

OCMStub([mockQuery findObjectsInBackgroundWithBlock:[OCMArg any]]).andDo(^(NSInvocation *invocation) {
    typedef void (^FindObjectsBlock)(NSArray *, NSError *error);

    FindObjectsBlock callback;
    [invocation getArgument:&callback atIndex:2];

    NSArray *results = evaluateQueryResultArrayFromDataArray(query, objects);

    callback(results, nil);
});

Но чтобы имитировать поведение SDK, я хотел бы иметь возможность вызывать этот метод несколько раз, и я ожидаю, что будут созданы различные фиктивные объекты. Это важно.

Я решил использовать возможности моделирования методов класса OCMock. Я обнаружил, что если я вызываю метод класса дважды, то насмешливое поведение применяется только в первый раз. Во второй раз издевательство неэффективно.

        NSLog(@"BEFORE first");
        PFQuery *firstQuery = [PFQuery queryWithClassName:@"THINGS"];
        NSLog(@"AFTER first");

        // Configure first query...

        NSLog(@"BEFORE second");
        PFQuery *secondQuery = [PFQuery queryWithClassName:@"THINGS"];
        NSLog(@"AFTER second");

        // Configure second query...

        expect(secondQuery).toNot.equal(firstQuery);            // OK

        [firstQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
            NSLog(@"THIS IS EXECUTED CORRECTLY %@", objects);   // OK; returns array as expected
        }];

        // The following query FAILS because the real underlying method is called.
        [secondQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
            NSLog(@"THIS DOESN'T EXECUTE RIGHT %@", objects);   // FAILS
        }];

След четко показывает .andDo() поведение выполняется только один раз.

2014-10-21 16:23:53.186 xctest[18380:209978] BEFORE first
2014-10-21 16:23:53.188 xctest[18380:209978] >> CREATED MOCK QUERY: OCPartialMockObject(PFQuery), 0x7fff5e74d980
2014-10-21 16:23:53.188 xctest[18380:209978] AFTER first
2014-10-21 16:23:53.188 xctest[18380:209978] BEFORE second
2014-10-21 16:23:53.188 xctest[18380:209978] AFTER second

Почему это?

1 ответ

Только один фиктивный объект может заглушить методы класса для данного класса. В документации говорится, что эффект не определен, когда два фиктивных объекта пытаются заглушить методы класса в одном и том же классе. В текущей реализации последующий макет заменит заглушку из предыдущего макета, что означает, что при создании макета для данного класса все заглушки методов класса, созданные другими макетами для того же класса, удаляются.

В вашем случае вы создаете макет для PFQuery заглушить queryWithClassName: метод. Когда вызывается эта заглушка, ее реализация создает новый (частичный) макет для PFQuery, В текущей реализации OCMock, как описано выше, этот новый макет теперь является единственным, который может заглушить методы класса на PFQuery, но он не объявил заглушку для queryWithClassName: метод...

Другие вопросы по тегам