Possible to reset state of dispatch_once in unit test, to make them run again

我只是一个虾纸丫 提交于 2019-11-27 05:34:02

问题


Is it possible to reset the state of dispatch_once code in a unit test tearDown?

I think it would be nice if our unit tests could run from a really clean state, but we are struggling with dispatch_once and some singletons made with dispatch once.


回答1:


I should note first that this isn't a good thing to do in any situation other than testing; even then, proceed with care -- AliSoftware provides some details and example code in comments below. See also the interesting answers at Can I declare a dispatch_once_t predicate as a member variable instead of static?, including some important information from the horse's mouth.

dispatch_once_t is a typedefd long. Its false value is 0. If you reset that flag to 0, dispatch_once() will run again. Your problem is "just" how to change the value of a static variable from another compilation unit. For this, I think you need a debug/unit test hook, like so:

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

(You could also move once_token up to file level and change it directly.)

Trying this out:

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

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

    @autoreleasepool {

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

Results in:

2012-06-07 18:45:28.134 ResetDispatchOnce[8628:403] That's what you get, folks.
2012-06-07 18:45:28.163 ResetDispatchOnce[8628:403] Making whoopie.
2012-06-07 18:45:28.164 ResetDispatchOnce[8628:403] Making whoopie.
2012-06-07 18:45:28.165 ResetDispatchOnce[8628:403] That's what you get, folks.
2012-06-07 18:45:28.165 ResetDispatchOnce[8628:403] Making whoopie.




回答2:


We too unit test our singletons and occasionally need to replace them with mock objects or reset them. I took Josh's answer and simplified it a bit further:

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


来源:https://stackoverflow.com/questions/10930044/possible-to-reset-state-of-dispatch-once-in-unit-test-to-make-them-run-again

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!