我一直在嘲笑对Parse Data后端的访问,并在OCMock遇到麻烦。
访问后端的主要数据访问机制是通过Parse
PFQuery
对象,由[PFQuery queryWithClassName:@"ClassName"]
构造。
这自然是一个很好的选择作为测试缝。
我想在这种情况下使用Partial mocks,原因我不会在这篇文章中介绍。
我没有返回真正的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
为什么会这样?
答案 0 :(得分:1)
对于给定的类,只有一个模拟对象可以存根类方法。文档说当两个模拟对象尝试在同一个类上存根类方法时,效果是未定义的。在当前的实现中,稍后的模拟将接管来自前一个模拟的存根,这意味着当您为给定类创建模拟时,将删除由同一类的其他模拟创建的所有类方法存根。
在您的情况下,您为PFQuery
创建一个模拟来存根queryWithClassName:
方法。当调用该存根时,其实现会为PFQuery
创建一个新的(部分)模拟。使用OCMock的当前实现,如上所述,新的mock现在是PFQuery
上可以存根类方法的唯一模拟,但它没有为queryWithClassName:
方法声明存根。 ..