简体   繁体   English

如何对NSFetchedResultsControllerDelegate进行单元测试?

[英]How to unit test NSFetchedResultsControllerDelegate?

I am trying to write unit tests for a view controller that implements the NSFetchedResultsControllerDelegate protocol . 我正在尝试为实现NSFetchedResultsControllerDelegate协议的视图控制器编写单元测试。 The first test of the implementation (after some other tests of this view controller) is to verify that a table row is inserted into the table view when a new object is inserted. 实现的第一个测试(此视图控制器的其他测试之后)是在插入新对象时验证表行是否已插入到表视图中。

My first implementation of the test was: 我对该测试的第一个实现是:

- (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];
}

When it tried to implement the functionality in the view controller to move to the green status (TDD), I wrote the following code: 当它尝试在视图控制器中实现将功能转换为绿色状态(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];
}

However, I couldn't get it to pass and the error is: 但是,我无法通过它,错误是:

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).

I tried to add one or more times the following line to the preparation part of the test with the same results. 我尝试将以下行添加一次或多次到测试的准备部分,结果相同。

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

As you can see, I am currently using OCUnit and OCMock . 如您所见,我目前正在使用OCUnitOCMock I would consider other tools only if it is impossible to create these kind of tests with this toolset, and in that case I would appreciate to an explanation of their limitations, should they exist. 仅当无法使用此工具集创建此类测试时,我才会考虑使用其他工具,在这种情况下,如果它们存在,我希望对它们的局限性做出解释。

As far as I understand, the mock is unable to "lie" about the nature of its class even when told to do so. 据我了解,即使被告知,该模拟也无法“撒谎”其类的性质。 Also, the error doesn't provide information about the class UITableView is looking for. 同样,该错误不提供有关UITableView正在查找的类的信息。 I know that it isn't a good practice for testing to use -isKindOfClass: , but it isn't my code. 我知道使用-isKindOfClass:并不是测试的好习惯,但这不是我的代码。

Thank you for your help. 谢谢您的帮助。

I've seen failures related to isKindOfClass: calls before, and they're usually the result of the way Apple implemented a certain feature in their own code. 我曾经见过与isKindOfClass:调用相关的失败,它们通常是Apple在自己的代码中实现特定功能的方式的结果。 The standard mockForClass: will reject any unexpected messages as a failing situation. 标准的mockForClass:将拒绝任何意外消息,作为失败情况。 A simple solution is to switch your mock to niceMockForClass: which is forgiving about such unexpected messages. 一个简单的解决方案是将您的模拟切换到niceMockForClass:niceMockForClass:此类意外消息。

Adding expectations for messages coming from third-party code would make your tests very coupled to external implementation details. 增加对来自第三方代码的消息的期望,将使您的测试与外部实现细节紧密结合。 Making sure isKindOfClass: gets called is clearly not an explicit requirement of your system. 确保isKindOfClass:被调用显然不是系统的明确要求。

One way to work around mocking objects that have concealed inner behaviors is to use a partial mock. 解决具有隐藏内部行为的模拟对象的一种方法是使用部分模拟。 In your case: 在您的情况下:

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

When I do this I usually change the name slightly and would call it tableViewPartial 当我这样做时,我通常会稍微更改名称,并将其命名为tableViewPartial

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM