Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test my models now that I am using Core Data?

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.

like image 713
Tony Eichelberger Avatar asked Dec 04 '09 21:12

Tony Eichelberger


People also ask

Should models be unit tested?

No. Such test provide little to no value as you will be creating model instances in other tests, for components that work with/use said models. As such, you don't need dedicated model tests because they will be tested indirectly many, many times.

Can unit tests use databases?

It is meant to make sure that definable modules of code work as expected. To test an application it is not enough to use unit tests. You must also perform functional testing and regression testing. Database access falls outside the scope of unit testing, so you would not write unit tests that include database access.

What is unit testing in .NET core?

In a nutshell: a unit test is code you can write to test your application code. Your web application will not have any knowledge of your test project, but your test project will need to have a dependency of the app project that it's testing. Unit Testing Project Dependencies.


2 Answers

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]];
  NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
  STAssertTrue([psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:NULL] ? YES : NO, @"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).

like image 173
Barry Wark Avatar answered Oct 11 '22 22:10

Barry Wark


I used the answer above by Barry Wark, but I had to do some modifications to make it work with the current projects Xcode 5, iOS 7.

The property stayed the same:

@interface SIDataTest : XCTestCase
    @property (nonatomic, retain) NSManagedObjectContext *moc;
@end

The setup had to actually had to change first of all to not release and secondly to provide a model URL.

- (void)setUp
{
    [super setUp];
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"SimpleInvoice" withExtension:@"momd"];
    NSManagedObjectModel *mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
    XCTAssertTrue([psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:NULL] ? YES : NO, @"Should be able to add in-memory store");
    self.moc = [[NSManagedObjectContext alloc] init];
    self.moc.persistentStoreCoordinator = psc;
}

Here is the example test case:

- (void)testCreateNew
{
    Invoice *newInvoice = [NSEntityDescription insertNewObjectForEntityForName:@"Invoice" inManagedObjectContext:self.moc];
    newInvoice.dueDate = [NSDate date];
    NSString* title = [[NSString alloc] initWithFormat:@"Invoice %@", @112];
    newInvoice.title = title;

    // Save the context.
    NSError *error = nil;
    if (![self.moc save:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        XCTFail(@"Error saving in \"%s\" : %@, %@", __PRETTY_FUNCTION__, error, [error userInfo]);
    }
    XCTAssertFalse(self.moc.hasChanges,"All the changes should be saved");
}
like image 22
mikebz Avatar answered Oct 11 '22 20:10

mikebz