2009-06-09 67 views
12

对于比赛我发展,我有触发通知时,他们的状态发生变化的几个模型类。然后,该视图订阅这些通知,并可以对它们做出反应。的OCUnit测试NSNotification交付

我做与的OCUnit模型我的单元测试,并希望断言预期的通知被张贴。对于这一点,我在做这样的事情:

- (void)testSomething { 
    [[NSNotificationCenter defaultCenter] addObserver:notifications selector:@selector(addObject:) name:kNotificationMoved object:board]; 

    Board *board = [[Board alloc] init]; 
    Tile *tile = [Tile newTile]; 

    [board addTile:tile]; 

    [board move:tile]; 

    STAssertEquals((NSUInteger)1, [notifications count], nil); 
    // Assert the contents of the userInfo as well here 

    [board release]; 
} 

的想法是,将NSNotificationCenter通过调用其addObject:方法通知添加到NSMutableArray

然而,当我运行它时,我发现addObject:正在发送到某个其他对象(而不是我的NSMutableArray),导致OCUnit停止工作。但是,如果我注释掉某些代码(例如release调用,或添加新的单元测试),则所有事情都按预期开始工作。

我假设这有邻与时机的问题,或NSNotificationCenter依靠以某种方式运行循环。

有没有任何建议来测试这个?我知道我可以在Board中添加一个setter,并注入我自己的NSNotificationCenter,但我正在寻找一种更快的方法来做到这一点(也许有一些关于如何动态替换NSNotificationCenter的技巧)。

+3

+1为单元测试通知的聪明方式! – 2011-07-21 13:53:43

回答

5

发现问题。测试通知时,您需要在测试完成后移除观察者。工作代码:

- (void)testSomething { 
    [[NSNotificationCenter defaultCenter] addObserver:notifications selector:@selector(addObject:) name:kNotificationMoved object:board]; 

    Board *board = [[Board alloc] init]; 
    Tile *tile = [Tile newTile]; 

    [board addTile:tile]; 

    [board move:tile]; 

    STAssertEquals((NSUInteger)1, [notifications count], nil); 
    // Assert the contents of the userInfo as well here 

    [board release]; 
    [[NSNotificationCenter defaultCenter] removeObserver:notifications name:kNotificationMoved object:board]; 
} 

如果无法删除观察者,测试运行和一些局部变量之后被释放,通知中心将尝试在运行任何后续的测试触发同样的通知时,通知那些老物件。

0

没有时间问题或runloop相关问题,因为一切都在你的代码是不同时发生的,应当立即执行。如果您使用NSNotificationQueue,NSNotificationCenter只会推迟通知传递。

我觉得一切都在您发布的片断正确。也许有可变数组'通知'的问题。你是否启动并保持正确?尝试手动添加一些对象,而不是使用通知技巧。

+0

我用[NSMutableArray arrayWithCapacity]分配数组。我不保留它(这是一个局部变量,所以NSAutoReleasePool不会释放它)。 – pgb 2009-06-09 21:01:05

+0

发现我的问题。我不会从NSNotificationCenter中移除观察者,因此当第二次测试运行时,它会尝试通知堆中不再存在的对象。 – pgb 2009-06-11 14:05:53

0

如果你怀疑你的测试有时间问题 - 你可能要考虑注入自己的通知机制到你的板子的对象(这可能只是一个现有的苹果版本的包装)。

即:

Board *board = [[Board alloc] initWithNotifier: someOtherNotifierConformingToAProtocol]; 

想必你的董事会对象的帖子一些通知 - 你会用你的代码通知注:

-(void) someBoardMethod { 

    // .... 

    // Send your notification indirectly through your object 
    [myNotifier pushUpdateNotification: myAttribute]; 
} 

在您的测试 - 你现在有间接的级别您可以使用它进行测试,因此您可以实现一个符合您的AP协议的测试类 - 并且可能会计算pushUpdateNotification:调用。在你真实的代码中,你封装了你可能已经在Board中进行通知的代码。

当然,这是在哪里MockObjects是有用的一个典型的例子 - 并有OCMock这也让你这样做,而不必有一个测试类做计数(参见:http://www.mulle-kybernetik.com/software/OCMock/

测试会可能有一条像这样的:

[[myMockNotifer expect] pushUpdateNotification: someAttribute]; 

另外,你可以考虑使用委托,而不是通知。这里有一套很好的pro/con幻灯片:http://www.slideshare.net/360conferences/nsnotificationcenter-vs-appdelegate

相关问题