views:

179

answers:

1

I have been developing an iphone application using a domain model, and have put off the persistence aspect of the app until now. Core Data looks like a really good solution since I already have a well defined model but I am running into a snag with my existing unit tests.

Here is simple example of what I have now:

- (void)test_full_name_returns_correct_string {
    Patient *patient = [[Patient alloc] init];  
    patient.firstName = @"charlie";
    patient.lastName = @"chaplin";
    STAssertTrue([[patient fullName] isEqualToString:@"charlie chaplin"], @"should have matched full name");
}

How can I make this work once my Patient object extends from NSManagedObject and uses @dynamic for the firstName and lastName properties?

Has anyone else run into this type of this with Core Data? Thanks.

+4  A: 

You need to build a Core Data stack, either within each method or in -setUp and then tear it down. Using an NSInMemoryPersistentStore will keep things fast and in-memory for your unit tests. Add a @property (nonatomic,retain) NSManagedObjectContext *moc to your TestCase subclass. Then:

- (void)setUp {
  NSManagedObjectModel *mom = [NSManagedObjectModel mergedModelFromBundles:[NSArray arrayWithObject:bundleContainingXCDataModel]];
  NSPersistentStore *psc = [[NSPersistentStore alloc] initWithManagedObjectModel:mom];
  STAssertTrue([psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:NULL], @"Should be able to add in-memory store");

  self.moc = [[NSManagedObjectContext alloc] init];
  self.moc.persistentStoreCoordinator = psc;

  [mom release];
  [psc release];

}

- (void)tearDown {
  self.moc = nil;
}

Your test method then looks like:

- (void)test_full_name_returns_correct_string {
    Patient *patient = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:self.moc];

    patient.firstName = @"charlie";
    patient.lastName = @"chaplin";
    STAssertTrue([[patient fullName] isEqualToString:@"charlie chaplin"], @"should have matched full name");
}

assuming your entity is named Person. There was a memory leak in your version of the method, by the way; patient should be -release'd in the non-Core Data version (insertNewObjectForEntityForName:managedObjectContext: returns an autoreleased instance).

Barry Wark
Thanks for the help. I will go this route. About the memory leak, I haven't bothered with cleaning up memory in my unit tests. It seems more readable to me without the releases. Is there a benefit for keeping the tests from leaking?
Tony Eichelberger
If your tests leak, it's really hard to use your unit test suite to test your *other* code for leaks. Instruments has a leak analyzer that's basically useless if you obscure *reall* leaks with unnecessary (but intentional) leaks of the same classes in your test code.
Barry Wark
I think you'll find that the retain/release code disappears from conscious vision after a while. I rarely notice it anymore--unless it's missing.
Barry Wark