3

在我的夹具设置中,我有以下内容

-(void)setUp{
    _vc = [OCMockObject partialMockForObject:[[InboxViewController alloc] init]];
    //stub out the view stuff
    [[_vc stub] removeTask:OCMOCK_ANY];
    [[_vc stub] insertTask:OCMOCK_ANY];
}

夹具中有 15 个测试,但是,我需要实际测试这 2 个方法是否被调用,所以我写了 2 个测试

-(void)someTest{
    [[_vc expect] removeTask:OCMOCK_ANY];
    [_vc removeAllTasksFromList:taskList notInList:newTaskList];
    [_vc verify];
}

但是那个测试失败了

我也试过

-(void)someTest{
    [[_vc stopMocking];
    [[_vc expect] removeTask:OCMOCK_ANY];
    [[_vc stub] removeTask:OCMOCK_ANY];
    [_vc removeAllTasksFromList:taskList notInList:newTaskList];
    [_vc verify];
} 

但测试仍然失败。我是否遗漏了什么,或者这就是 OCMock 的工作原理?

我能让它工作的唯一方法就是这样

-(void)someTest{
    //re create and init the mock object 
    _vc = [OCMockObject partialMockForObject:[[InboxViewController alloc] init]]; 
    [[_vc expect] removeTask:OCMOCK_ANY]; 
    [[_vc stub] removeTask:OCMOCK_ANY]; 
    [_vc removeAllTasksFromList:taskList notInList:newTaskList]; 
    [_vc verify]; 
}
4

5 回答 5

8

也许文档应该更清楚。stopMocking对部分模拟所做的是将真实对象(在您的情况下为 InboxViewController)恢复到其原始状态。调用stopMocking不会重置模拟对象,这意味着它不会清除存根和期望。你总是可以调用stopMocking然后为同一个真实对象创建一个新的模拟。

正如在另一个答案中所指出的那样,通常最好避免存根和期望相同的方法,但是如果必须这样做,请确保在存根之前设置期望;否则存根将处理调用,而期望永远不会看到它们。

我知道传统上很多人建议使用 setup 方法来设置测试对象。多年来,我的个人经验是,这通常是不值得的。在每个测试中保存几行代码可能看起来很有吸引力,但最终它确实会在各个测试之间产生耦合,从而使套件更加脆弱。

于 2013-05-25T17:21:25.810 回答
2

似乎您需要创建一个新的模拟来期望您已经存根的方法。我建议您重新考虑在所有测试用例中使用部分模拟,如果您愿意,请提取以下内容:

[[_vc stub] removeTask:OCMOCK_ANY];
[[_vc stub] insertTask:OCMOCK_ANY];

进入辅助方法并从您真正需要的测试中调用该方法,将其从您的 setUp 方法中删除。

而且,小提示:),您应该[super setUp]在实施开始时调用setUp

于 2013-05-24T12:34:16.157 回答
2

您可以执行以下操作以从 OCMockObject 中删除存根,这将使您能够将stub代码保留在您-(void)setUpexpect.

将以下类别添加到您的测试以返回 OCMockObject ivar。

@interface OCMockObject (Custom)
- (void)removeStubWithName:(NSString*)stubName;
@end

@implementation OCMockObject (Custom)
- (void)removeStubWithName:(NSString*)stubName {
    NSMutableArray* recordersToRemove = [NSMutableArray array];
    for (id rec in recorders) {
        NSRange range = [[rec description] rangeOfString:stubName];
        if (NSNotFound == range.location) continue;
        [recordersToRemove addObject:rec];
    }
    [recorders removeObjectsInArray:recordersToRemove];
}
@end

示例用法:确保在添加期望之前删除方法的存根。

-(void)someTest{
    [_vc removeStubWithName:@"removeTask"];
    [[_vc expect] removeTask:OCMOCK_ANY];
    [_vc removeAllTasksFromList:taskList notInList:newTaskList];
    [_vc verify];
}

这将使您的测试按预期运行。

这是一个有用的 hack,至少在 OCMock 开发人员允许此功能之前是这样。

于 2014-04-21T15:48:17.900 回答
1

有几点需要注意:

  1. 您应该保留对您正在模拟的实际对象的引用。在模拟参考上调用模拟设置/验证,以及在实际对象上测试的方法。
  2. 您不应该存根并期望相同的方法调用。存根将匹配,并且期望不会通过。

我假设您正在插入,setUp因为您有一些在测试中可能会或可能不会被调用的方法。如果是这样,您可以像这样构建您的测试:

static InboxViewController *_vc;
static id mockInbox;

-(void)setUp{
    _vc = [[InboxViewController alloc] init];
    mockInbox = [OCMockObject partialMockForObject:_vc];
    //stub out the view stuff
    [[mockInbox stub] removeTask:OCMOCK_ANY];
    [[mockInbox stub] insertTask:OCMOCK_ANY];
}

-(void)someTest{
    [[mockInbox expect] somethingIExpectForThisTest:OCMOCK_ANY];
    [_vc removeAllTasksFromList:taskList notInList:newTaskList];
    [mockInbox verify];
}

-(void)someOtherTest{
    [[mockInbox expect] someOtherThingIExpectForThisTest:OCMOCK_ANY];
    [_vc doSomethingElse];
    [mockInbox verify];
}
于 2013-05-25T16:49:35.230 回答
0

给定存根和期望调用的顺序对于部分模拟很重要。在期望方法之前对方法进行存根将意味着发送到存根方法的任何消息将永远不会满足期望。

另一方面,stopMocking 只是意味着部分模拟将停止与真实对象关联。模拟仍然保留其记录器(存根和期望)并照常工作。您可以通过发送removeAllTasksFromList:notInList到您的真实对象来验证这一点,在这种情况下,它没有分配给任何变量。在这种情况下,您会看到消息确实到达了您的对象的实现。不过,模拟仍然会使验证调用失败。通常,您应该在真实对象上调用方法。部分模拟仍然会拦截消息。

如另一个答案中所述,解决此问题的最佳方法是创建一个新的部分模拟并在存根方法之前调用期望。你甚至可以实现一个助手:

- (void) setUpPartialMockWithExpect:(BOOL)needExpect {
    _vc = [OCMockObject partialMockForObject:[[InboxViewController alloc] init]];

    if( needExpect )
        [[_vc expect] removeTask:OCMOCK_ANY];

    //stub out the view stuff
    [[_vc stub] removeTask:OCMOCK_ANY];
    [[_vc stub] insertTask:OCMOCK_ANY];
}

并在你的每一个中调用它-(void)test...

于 2013-05-25T16:33:51.447 回答