Почему мое ожидание 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:
метод...