Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Testing: dispatch_once get called twice. First in App, second in Test. Problems with Observers

I have a singelton class which will be create in the app delegate.

When i run XCTTests then its get create a second time.

+ (instancetype)urlSchemeManager
{
    static dispatch_once_t onceToken;
    static UrlSchemeManager* _sharedInstance;

    dispatch_once(&onceToken, ^{

        _sharedInstance = [UrlSchemeManager new];


    });
    return _sharedInstance;
}

This is resulting in two different instances. This was no problem if i just use it for unit test. But in the integration test, when i register an observer for urlSchmemeManager i get a EXC_BAD_ACCESS, because it was already observed by the rootViewController (in the UI).

In RootViewController:

UrlSchemeManager * schemeManager = [GlobalSpace globalSpace].urlSchemeManager;
[schemeManager addObserver:self forKeyPath:OBSERVER_KEY_URL_SCHEME_MANAGER_CONTENT_MORE options:NSKeyValueObservingOptionNew context:nil];

Does anyone has an idea how i can get around this problem?

like image 358
Sam Avatar asked Jan 09 '14 08:01

Sam


2 Answers

I had the same problem with dispatch_once being called multiple times when running a test suite. I fixed it by removing the singleton class from the Target Membership of the Test.

Once you've done that make sure that your test target is dependent on your application in "Build Phases" so that the test still knows about the class.

After that, the test should run and the singleton should only be instantiated one time.

like image 137
Mike Sabatini Avatar answered Sep 19 '22 14:09

Mike Sabatini


Mike's answer is correct! Just adding some more info. This is a really tricky issue. Seems to be the case that app target and the test target are compiled separated. In runtime, however, the binary for the tests is injected into the app's space. Because it is usually the compiler's job to detect duplicate symbols, and the compilation process is different, it can happen that you have two instances of a class at runtime. Each instance of the class with it's own set of static variables. This is super weird. Tons of weird behaviors can stem from this. Including the double dispatch_once_t execution.

I faced this problems in my cocoapods clases. All pods in your Podfile are, by default, linked into all targets. As a result you will have duplicate clases at runtime when running XCTest. The solution is to specify your pods per target. In my case, for example. I did:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.0'

target 'MyApp', :exclusive => true do
    pod 'AFNetworking'
    pod 'ObjectiveRecord', :head
    ...
end

target 'MyApp Tests', :exclusive => true do
  pod 'KIF', '~> 3.0', :configurations => ['Debug']
end

inhibit_all_warnings!

I specifically had issues with the singleton pattern in ObjectiveRecord. Because the core data context manager class was created twice, different sections of my app were not seeing the same data.

like image 26
fsaint Avatar answered Sep 19 '22 14:09

fsaint