2012-06-07 3 views
29
を実行して作るために

それはユニットテストのティアダウンにdispatch_onceコードの状態をリセットすることは可能ですか?可能なユニットテストでdispatch_onceの状態をリセットするために、彼らは再び

私は、私たちのユニットテストは本当にきれいな状態から実行することができれば、それはいいだろうと思うが、我々はdispatch_once、一度の発送で作られたいくつかのシングルトンで苦労しています。

答えて

42

私は、これは、試験以外の状況で行うには良いことではないことを最初に注意すべきです。 AliSoftwareは以下のコメントにいくつかの詳細とサンプルコードを提供しています。いくつかの重要な情報を含む興味深い解答Can I declare a dispatch_once_t predicate as a member variable instead of static?も参照してください。from the horse's mouth

dispatch_once_tは、typedefdである。その偽の値は0です。そのフラグを0にリセットすると、dispatch_once()が再び実行されます。あなたの問題は、静的変数の値を別のコンパイル単位から変更する方法だけです。このために、私はそうのように、あなたはデバッグ/ユニットテスト用のフックが必要だと思う:

MakeWhoopie.h

#import <Foundation/Foundation.h> 

void makeWhoopie(void); 

#ifdef DEBUG 
void resetDispatchOnce(void); 
#endif 

MakeWhoopie.m

#include "MakeWhoopie.h" 

static dispatch_once_t * once_token_debug; 

void makeWhoopie(void) 
{ 

    static dispatch_once_t once_token; 
    once_token_debug = &once_token; // Store address of once_token 
             // to access it in debug function. 
    dispatch_once(&once_token, ^{ 
     NSLog(@"That's what you get, folks."); 
    }); 

    NSLog(@"Making whoopie."); 
} 

#ifdef DEBUG 
void resetDispatchOnce(void) 
{ 
    *once_token_debug = 0; 
} 
#endif 

(また、ファイルまでonce_tokenを移動することができますレベルを変更して直接変更してください)

これを試してみてください:

#import <Foundation/Foundation.h> 
#import "MakeWhoopie.h" 

int main(int argc, const char * argv[]) 
{ 

    @autoreleasepool { 

     makeWhoopie(); 
     makeWhoopie(); 
     resetDispatchOnce(); 
     makeWhoopie(); 
    } 
    return 0; 
} 

2012-06-07 18:45:28.134 ResetDispatchOnce [8628:403]それはあなたが得るものですが、皆さんで

結果。
2012-06-07 18:45:28.163 ResetDispatchOnce [8628:403] whoopieを作成する。
2012-06-07 18:45:28.164 ResetDispatchOnce [8628:403] whoopieを作成しています。
2012-06-07 18:45:28.165 ResetDispatchOnce [8628:403]それはあなたが得るものです、皆さん。
2012-06-07 18:45:28.165 ResetDispatchOnce [8628:403] whoopieを作ります。

+0

「ディスパッチ_オン」のポイントはスレッドセーフであるということです。 '設定することで問題は* once_token_debug = 0 'この方法では、それはあなたが'自分でonceToken'設定されている間、別のスレッドが '(...、&onceToken)' dispatch_onceを使用の場合には全くスレッドセーフではないということです。どのように私たちはそのような問題を防ぐことができますか? – AliSoftware

+0

@AliSoftware:問題のシナリオは単体テストであり、これは私がこの手順で提案する_only_の使用です。大きなプログラムの実行間に作成されるようにクリーンスレートを作成するために、トークンはテストの間にリセットされています。テスト間でプログラムが実行されていないため、スレッド化は問題にはなりません。 –

+0

私はこれを理解しましたが、誰もがそれを認識し、スレッドセーフでない方法でこれを使用するように誘惑されてはいけません。また、ユニットテストでさえ、これがスレッドセーフではない場合があります。特に、テストがひどくコード化されていて、テストが終了した後でも非同期アクションが実行された場合( 'dispatch_after(10s、^ {テストがタイムアウトに達するとsharedInstance * /} 'を使用するsthgは失敗し、停止して' tearDown'に到達しますが、sharedInstanceを使用しているブロックは近い将来に... tearDownの後に送出されます。 – AliSoftware

4

私たちも単体テストをテストし、ときには疑似オブジェクトで置き換えるかリセットする必要があります。私はジョシュの答えをとり、それをさらに単純化した。

static ArticleManager *_sharedInstance = nil; 
static dispatch_once_t once_token = 0; 

+(ArticleManager *)sharedInstance { 
    dispatch_once(&once_token, ^{ 
     if (_sharedInstance == nil) { 
      _sharedInstance = [[ArticleManager alloc] init]; 
     } 
    }); 
    return _sharedInstance; 
} 

+(void)setSharedInstance:(ArticleManager *)instance { 
    if (instance == nil) once_token = 0; 
    _sharedInstance = instance; 
} 
+0

私はこのようにしなければならない理由を説明するブログ記事を書いた:http://twobitlabs.com/2013/01/objective-c-singleton-pattern-unit-testing/ – ToddH

+0

私の答え。 SOに掲載された私の - または誰のコードも、逐語的であっても修正されていても、[attribution](http(http://creativecommons.org/licenses/by-sa/3.0/) ://blog.stackoverflow.com/2009/06/attribution-required/)。この場合、そのメカニズムは非常に簡単なので言及に値することはほとんどありませんが、リンクしていなければ、SOの回答に基づいてブログを書く習慣に入らないようにしてください。 –

+0

実際、dispatch_once内の== nilチェックは冗長なようです。 _sharedInstanceがnilに設定されている場合にのみ、once_tokenをリセットします。 – Vitali

関連する問題