为什么我的OCMock和Do期望只是第一次调用

时间:2014-10-21 15:38:53

标签: objective-c ocmock

我一直在嘲笑对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

为什么会这样?

1 个答案:

答案 0 :(得分:1)

对于给定的类,只有一个模拟对象可以存根类方法。文档说当两个模拟对象尝试在同一个类上存根类方法时,效果是未定义的。在当前的实现中,稍后的模拟将接管来自前一个模拟的存根,这意味着当您为给定类创建模拟时,将删除由同一类的其他模拟创建的所有类方法存根。

在您的情况下,您为PFQuery创建一个模拟来存根queryWithClassName:方法。当调用该存根时,其实现会为PFQuery创建一个新的(部分)模拟。使用OCMock的当前实现,如上所述,新的mock现在是PFQuery上可以存根类方法的唯一模拟,但它没有为queryWithClassName:方法声明存根。 ..