2009-06-09 11 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とOに持っていると仮定しています。

これをテストする推奨はありますか?私はにセッターを追加し、私自身のNSNotificationCenterを注入することができると知っていますが、私はそれを行うより速い方法を探しています(NSNotificationCenterを動的に置き換える方法についてのいくつかのトリックかもしれません)。

+3

+1ユニットテスト通知の巧妙な方法です! –

答えて

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

コード内のすべてが非同期であり、すぐに実行する必要があるため、タイミングの問題や実行ループに関する問題はありません。 NSNotificationCenterは、NSNotificationQueueを使用する場合にのみ、通知配信を延期します。

あなたが投稿したスニペットのすべてが正しいと思います。多分、可変配列の「通知」に問題があります。それを正しく初期化して保持しましたか?通知トリックを使用する代わりに、手動でオブジェクトを追加してみてください。

+0

私は[NSMutableArray arrayWithCapacity]で配列を割り当てています。私はそれを保持していません(それはローカル変数なので、NSAutoReleasePoolはそれをまだリリースしません)。 – pgb

+0

私の問題が見つかりました。私はNSNotificationCenterからオブザーバーを削除していないので、2回目のテストが実行されると、ヒープに存在しなくなったオブジェクトを通知しようとします。 – pgb

0

あなたのテストはタイミングの問題している疑いがある場合 - あなたは(おそらく既存のリンゴ版の単なるラッパーである)あなたのボードオブジェクトに独自の通知メカニズムを注入することを検討する必要があります。

です:

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

おそらくあなたのボードオブジェクトの記事いくつかの通知 - あなたがそのコードに通知を注入を使用します。テストで

-(void) someBoardMethod { 

    // .... 

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

- あなたは今、間接のレベルを持っていますテスト用に使用できるので、あなたのAProtocolに準拠したテストクラスを実装できます。また、pushUpdateNotification:呼び出しをカウントアップすることもできます。あなたの実際のコードでは、おそらくすでに通知を行うBoardにあるコードをカプセル化します。当然の

このMockObjectsが有用でどこの古典的な例である - とOCMockがうまくあなたがカウントを行うためにテストクラスを持つことなく、これをやらせているがあり(参照:http://www.mulle-kybernetik.com/software/OCMock/

テストですおそらく次のような行があります。

[[myMockNotifer expect] pushUpdateNotification: someAttribute]; 

また、通知ではなく代理人の使用を検討することもできます。ここには良いプロ/コンセットがあります:http://www.slideshare.net/360conferences/nsnotificationcenter-vs-appdelegate

+0

Viewレイヤーのアニメーションを非同期でトリガーするので、このケースでは正常に通知されると思います。私はテストコードとメインクラスを単純化するために私のカスタム通知クラスを注入するのを避けようとしていましたが、それはこれまでの唯一の選択肢のようです。 – pgb

関連する問題