如何对NSFetchedResultsControllerDelegate进行单元测试?

时间:2013-04-21 18:32:42

标签: ios unit-testing core-data ocunit ocmock

我正在尝试为实现NSFetchedResultsControllerDelegate protocol的视图控制器编写单元测试。实现的第一个测试(在此视图控制器的一些其他测试之后)是验证在插入新对象时是否将表行插入到表视图中。

我的第一次测试是:

- (void) setUp {
    [super setUp];

    sut = [[JODataTableViewController alloc] init];
    fetchedResultsCtrlrMock = [OCMockObject niceMockForClass:[NSFetchedResultsController class]];
    NSError *__autoreleasing *err = (NSError *__autoreleasing *) [OCMArg anyPointer];
    [[[fetchedResultsCtrlrMock expect] andReturnValue:OCMOCK_VALUE((BOOL){YES})] performFetch:err];
    [sut setValue:fetchedResultsCtrlrMock forKey:@"fetchedResultsController"];
    [sut view]; // This invokes viewDidLoad.
}

- (void) tearDown {
    sut = nil;

    [super tearDown];
}

- (void) testObjectInsertedInResultsAddsARowToTheTable {
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    id tableViewMock = [OCMockObject mockForClass:[UITableView class]];
    sut.tableView = tableViewMock;
    [[tableViewMock expect] insertRowsAtIndexPaths:@[indexPath]
                                  withRowAnimation:UITableViewRowAnimationLeft];

    [sut controller:nil didChangeObject:nil
        atIndexPath:nil
      forChangeType:NSFetchedResultsChangeInsert
       newIndexPath:indexPath];

    [tableViewMock verify];
}

当它试图在视图控制器中实现功能以转移到绿色状态(TDD)时,我编写了以下代码:

- (void) controllerWillChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView beginUpdates];
}


- (void) controller:(NSFetchedResultsController *)controller
    didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath
      forChangeType:(NSFetchedResultsChangeType)type
       newIndexPath:(NSIndexPath *)newIndexPath {
    UITableViewCell *cell;

    switch (type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertRowsAtIndexPaths:@[newIndexPath]
                                  withRowAnimation:UITableViewRowAnimationLeft];
            break;

    }
}


- (void) controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView endUpdates];
}

但是,我无法通过,错误是:

Test Case '-[JODataTableViewControllerTests testObjectInsertedInResultsAddsARowToTheTable]' started.
Unknown.m:0: error: -[JODataTableViewControllerTests testObjectInsertedInResultsAddsARowToTheTable] : OCMockObject[UITableView]: unexpected method invoked: isKindOfClass:<??> 
Test Case '-[JODataTableViewControllerTests testObjectInsertedInResultsAddsARowToTheTable]' failed (0.001 seconds).

我尝试将以下一行或多次添加到测试的准备部分,但结果相同。

[[[tableViewMock expect] andReturnValue:OCMOCK_VALUE((BOOL){YES})] isKindOfClass:[OCMArg any]];

如您所见,我目前正在使用OCUnitOCMock。只有在用这个工具集创建这些测试时我才会考虑其他工具,在这种情况下,如果它们存在,我会很感激地解释它们的局限性。

据我了解,即使被告知这样做,模仿也无法“欺骗”其阶级的性质。此外,该错误不提供有关UITableView类正在查找的信息。我知道测试使用-isKindOfClass:不是一个好习惯,但这不是我的代码。

感谢您的帮助。

2 个答案:

答案 0 :(得分:0)

我之前看到过与isKindOfClass:调用相关的失败,而这些失败通常是Apple在自己的代码中实现某个功能的方式的结果。标准mockForClass:将拒绝任何意外消息作为失败的情况。一个简单的解决方案是将模拟切换为niceMockForClass:,这是对这些意外消息的原谅。

为来自第三方代码的消息添加期望将使您的测试与外部实现细节非常相关。确保调用isKindOfClass:显然不是您系统的明确要求。

答案 1 :(得分:0)

解决隐藏内部行为的模拟对象的一种方法是使用部分模拟。在你的情况下:

id tableViewMock = [OCMock partialMockForObject:[[UITableView alloc] init]];

当我这样做时,我通常会略微更改名称,并将其称为tableViewPartial