2009-06-09 70 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

相關問題