Previous Entry Share Next Entry
Unit testing and Core Data
latest
chanson
Mike Zornek asks about unit testing and Core Data. I've been meaning to write about this, so this is the perfect opportunity to do so.

Writing unit tests against your model and code that uses Core Data is easy. For example, it's trivial to load your compiled model in a unit test:
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
Not only that, but you can introspect it:
NSArray *entities = [model entities];
And you can do this all the way down to the property level. This means that it's possible to assert that your entire model is set up the way you expect it to be. For example, you can make sure that your Employee entity has a mandatory salary attribute with a minimum value of 1 and a type of NSDecimalAttributeType, and descends from a Person entity that has a mandatory name attribute with a minimum length of 1 and a default value of "name."

But how do you test your use of Core Data? You just use Core Data in your tests as you would in your project. For example, to instantiate a complete Core Data "stack" (as it's sometimes referred to):
NSManagedObjectModel *model;
NSPersistentStoreCoordinator *coordinator;
NSManagedObjectContext *context;

model = [[NSManagedObjectModel alloc] initWithContentsOfURL:urlToModel];
coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:coordinator];
To instantiate managed objects associated with that context from entities in your model:
NSManagedObject *employee;

employee = [NSEntityDescription insertNewObjectForEntityForName:@"Employee"
                                 inManagedObjectContext:context];
This gives you an autoreleased Employee (assuming your context's coordinator's model has an Employee entity, of course). It's that easy.

You can then do things like check that this object was created with the correct defaults (e.g. the ones specified in your model), that it posts KVO notifications properly for properties where you care about such things, and so on. You can even add a persistent store to the coordinator and test that saving and loading work (and don't work when they're supposed to fail, of course) just by using normal Core Data and Foundation APIs.

You can multiply the full power of data modeling with the full power of unit testing and test driven development. It kicks ass.

Core Data is just so freakin cool. Words alone can't describe.

Difficulty with this code

(Anonymous)

2006-07-24 08:50 pm (UTC)

Have there been any changes to the way this ought to work since you wrote it? I'm having a hell of a time getting unit testing to play nicely with Core Data. I don't want to pollute your comments with my inane problems, but would love some insight if you have the time.

Re: Difficulty with this code

chanson

2006-09-11 04:15 pm (UTC)

There haven't been. What kinds of problems are you running into? Your best bet is to ask questions on either the Cocoa-Dev or the Xcode-Users mailing list, both of which are hosted by Apple.

Persistent store type

(Anonymous)

2009-07-19 12:06 am (UTC)

In your example, you do not stipulate the type of persistent storage. Is the default being memory?

Other question, would you initialize the core data stack in the unit test Setup method or somewhere else (e.g. initializer)?

Setting up a persistent store for testing

garethtownsend.info

2009-08-17 09:14 am (UTC)

What is the best way to set up a persistent store for testing purposes?


I'm having trouble with exactly this. I have a question on StackOverflow that I believe you commented on: http://stackoverflow.com/questions/2842258/unit-testing-model-classes-that-inherit-from-nsmanagedobject.

Thanks!

this + http://stackoverflow.com/questions/1849802 = win! :)

Cool. Except that those are not unit tests. Now, I'm not saying they are not good tests, but definitely not unit tests.