可以在单元测试中重置dispatch_once的状态,使它们再次运行

时间:2012-06-07 10:30:31

标签: objective-c grand-central-dispatch

是否可以在单元测试tearDown中重置dispatch_once代码的状态?

我认为如果我们的单元测试可以从一个非常干净的状态运行会很好,但是我们正在努力使用dispatch_once以及一些发送一次的单例。

2 个答案:

答案 0 :(得分:44)

首先我应该注意,除了测试之外,在任何情况下这都不是一件好事。即便如此,请小心处理 - AliSoftware在下面的评论中提供了一些细节和示例代码。另请参阅Can I declare a dispatch_once_t predicate as a member variable instead of static?上的有趣答案,包括一些重要信息from the horse's mouth

dispatch_once_ttypedef d 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]呐喊   2012-06-07 18:45:28.164 ResetDispatchOnce [8628:403]呐喊   2012-06-07 18:45:28.165 ResetDispatchOnce [8628:403]这就是你得到的,伙计们。
  2012-06-07 18:45:28.165 ResetDispatchOnce [8628:403]制作whoopie。

答案 1 :(得分:4)

我们也对单身人士进行单元测试,偶尔需要用模拟对象替换它们或重置它们。我接受了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;
}