It’s been a while since I was deep into CoreData, but here I am again. And this time, I’m writing proper unit tests.
Things were going pretty well – I had about 50 test cases with a few related asserts in each – until I started messing with saving, and fetching tests. I ran into some odd behavior where sometimes a particular test would pass, other times fail with different results in one assertion. All I was doing was creating a managed object, setting a relationship, saving, and reading back to check for proper order. Pretty trivial stuff.
I looked deeper.
If I ran just that one test case by itself, it would always pass. If I ran “All Tests”, it would always fail. Sometimes I would get several fetched objects when only one was expected. Sometimes those would be in different orders.
As I dug deeper, I attempted to reset my context on each test case
[self.managedObjectContext reset];
No luck.
To this point, my managedObjectContext has been a singleton – to follow the pattern of the rest of the appellation. I had a hunch the problem might be there – especially if multiple tests are running (concurrently, even?).
+(instancetype)inMemoryManager { static DataManager *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[DataManager alloc] init]; NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[sharedInstance managedObjectModel]]; NSError *error; if (![persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); } sharedInstance.persistentStoreCoordinator = persistentStoreCoordinator; }); return sharedInstance; }
I tried creating a separate for-testing-only manager that creates a new stack each time so I could hopefully avoid these apparent concurrency issues.
+(instancetype)inMemoryManagerForTesting { DataManager *dataManager = [[DataManager alloc] init]; NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[dataManager managedObjectModel]]; NSError *error; if (![persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:&error]) { NSLog(@"Unresolved error %@, %@", error, [error userInfo]); } dataManager.persistentStoreCoordinator = persistentStoreCoordinator; return dataManager; }
That seems to do the trick, though I would have preferred sticking with the singleton. Don’t use CoreData stack singletons. Turns out @orta suggests the same [tweet] [blog post].