Core Data and OCUnit

2005-06-06 02:00

I've been finding a scarcity of posts on how to write Unit Tests for generated code. Specifically, I've just started working with Core Data on OS X 10.4. It's a fantastic data modeling tool. But given it's GUI nature, it's not immediately obvious how you write test-first code for it. You're supposed to write tests before you write a line of code. But in this case, you need to write test code before you use the GUI to generate the code for you.

It turns out that model created by Core Data supports reflection. It's not that pretty, but I've got a start on it. If you need to write some of your own Core Data test code, hopefully this will help you get on your way:

I'm assuming you've already created your Core Data project and a test project. I couldn't find a clean way of having the test project compile the Core Data model and make it work. So I decided to turn things around and have the Test project depend on the main project. To do this, I made the main project a dependency of the test project. I now build the test project, which builds the main project first. Now I've got the main program's bundle to browse. Since this is what I actually want to browse, this seems cleaner to me than the usual model of having the test recompile the classes in the main program.

Here's the function I'm starting with to test the model. Note that it's trivial at this point, since I just began coding this morning. This code will load the model from the main bundle, get the entities from the model, and query an attribute. It's using the "BFI" pattern (Brute Force and Ignorance).

// Load model from main application
NSBundle *ltBundle = [NSBundle bundleWithPath:@"/PT/Output/LifeTank/LifeTank.app"];
STAssertNotNil (ltBundle, @"Bundle is loaded");
NSArray *bundleArray = [NSArray arrayWithObject:ltBundle];
STAssertEquals([bundleArray count], (unsigned)1, @"1 object in array");
NSManagedObjectModel *model =
[NSManagedObjectModel mergedModelFromBundles:bundleArray];
STAssertNotNil (model, @"Model is created");

// Check model for correct number of entities
NSDictionary *entityDict = [model entitiesByName];
STAssertEquals([entityDict count], (unsigned)1, @"Correct number of entities");

// Validate 'WorkItem'
NSString * entityName = @"WorkItem";
NSEntityDescription *entityDesc = [entityDict objectForKey:entityName];
STAssertNotNil (entityDesc, @"%@ does not exist", entityName);
NSDictionary *wiDict = [entityDesc attributesByName];
NSString *attrName = @"title";
NSAttributeDescription *attrDesc = [wiDict objectForKey:attrName];
STAssertNotNil (attrDesc, @"%@.%@: attribute missing", entityName, attrName);

It's fairly obvious from this starting point that there is going to be a lot of cookie cutter code to test an entire Core Data framework. The next step is to build an XML representation of the model that can be read in by the tests and used to validate the Core Data model. That will provide a redundant way of validating that what I expect to be in the model is in sync, and make updating the test as trivial as changing the XML. I'll post that once I'm done with it.