web-dev-qa-db-ja.com

ユニットテストでdispatch_onceの状態をリセットして、再度実行することができます

ユニットテストtearDownでdispatch_onceコードの状態をリセットすることは可能ですか?

ユニットテストを本当にクリーンな状態から実行できればいいと思いますが、dispatch_onceとdispatchonceで作成されたシングルトンに苦労しています。

32

最初に、これはテスト以外の状況で行うのは良いことではないことに注意する必要があります。それでも、注意して進めてください。AliSoftwareは、以下のコメントでいくつかの詳細とサンプルコードを提供しています。 _dispatch_once_t_述語を静的ではなくメンバー変数として宣言できますか? 、いくつかの重要な情報 馬の口から

_dispatch_once_t_はtypedefd longです。そのfalse値は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を作成しています。

44
Josh Caswell

私たちもシングルトンをユニットテストしており、時にはそれらをモックオブジェクトに置き換えるかリセットする必要があります。 Joshの答えを取り入れて、もう少し単純化しました。

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;
}
4
ToddH