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].