So...I'm trying to get unit tests set up in my iPhone App but I'm having some issues. I'm trying to test my model classes but they inherit directly from NSManagedObject. I'm sure this is a problem but I don't know how to get around it.
Everything is building and running as expected but I get this error when calling any method on the class I'm testing:
Unknown.m:0:0 unrecognized selector sent to instance 0xc2b120
If I follow this structure to create my object in my tests I end up with another error entirely but it still doesn't help me.
If I instantiate my model like this:
entry = [[TimeEntry alloc]
initWithEntity:nil
insertIntoManagedObjectContext:nil];
Then I end up with this error at runtime:
An NSManagedObject of class 'TimeEntry' must have a valid NSEntityDescription.
If I try it like this:
entry = [[TimeEntry alloc] init];
Then I end up with this error:
unrecognized selector sent to instance 0xc2b120
And if I follow the pattern laid out here:
model = [[NSManagedObjectModel mergedModelFromBundles: nil] retain];
NSLog(@"model: %@", model);
coord = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: model];
store = [coord addPersistentStoreWithType: NSInMemoryStoreType
configuration: nil
URL: nil
options: nil
error: NULL];
ctx = [[NSManagedObjectContext alloc] init];
[ctx setPersistentStoreCoordinator: coord];
entry = (TimeEntry *)[NSEntityDescription insertNewObjectForEntityForName:@"TimeEntry" inManagedObjectContext:ctx];
Then I get this error:
could not locate an entity named 'TimeEntry' in this model.
Basically my question is this: how can I test a class that inherits from NSManagedObject?
In order to instantiate an NSManagedObject, you need an entity. Thus what you first tried — either passing nil
for the entity or using bare -init
(which isn't supported on NSManagedObject) — didn't work. You're doing the right thing by using -[NSEntityDescription insertNewObjectForEntityForName:inManagedObjectContext:]
to create the object, you just need to:
Note that unless you specifically want to test save/delete validation, you generally won't need to add a persistent store to your coordinator. (And if you're using an SQLite persistent store in your app, I'd strongly suggest using one in your tests too; the different persistent store types have different performance characteristics and supported queries.)
For ensuring your data model is loaded, you'll find it much more fruitful I think to actually specify the URL to load it from, instead of just hoping that you've put it in the right place and that -mergedModelFromBundles:
will do the right thing. I'd make it a member of your unit test bundle target, so it's compiled into your unit test bundle's Resources. That way you can just use an appropriate NSBundle method to get the path or URL to it.
Finally, you're going to want to put the set-up of your Core Data persistence stack — the model, persistent store coordinator, and a scratch context — in a -setUp
method in your test case. Or in a -setUp
method of a test case base class, if you want to create more than one test case class. (The same goes for tear-down of the persistence stack and -tearDown
methods, of course.)
I created a Sample for a Core Data Test environment on github http://github.com/mbrugger/CoreDataDependentProperties/blob/master/LPAutomatedObserving/Tests/ManagedObjectSenTestCase.m
Inherit your testcases from ManagedObjectSenTestCase.m/h and adjust the following two lines with your test target bundle identifier and data model name
NSBundle* bundle = [NSBundle bundleWithIdentifier:@"com.yourcompany.ModelTest"];
NSString* path = [bundle pathForResource:@"DataModel" ofType:@"mom"];
Code samples:
-(void) setUp
{
pool = [[NSAutoreleasePool alloc] init];
NSMutableSet *allBundles = [[[NSMutableSet alloc] init] autorelease];
[allBundles addObjectsFromArray:[NSBundle allBundles]];
NSBundle* bundle = [NSBundle bundleWithIdentifier:@"com.yourcompany.ModelTest"];
NSString* path = [bundle pathForResource:@"DataModel"
ofType:@"mom"];
NSURL* modelURL = [NSURL URLWithString:path];
self.model = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] autorelease];
self.coordinator = [[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.model] autorelease];
LPManagedObjectContext* tempContext = [[[NSManagedObjectContext alloc] init] autorelease];
[tempContext setPersistentStoreCoordinator:coordinator];
[tempContext setRetainsRegisteredObjects:YES];
self.context = tempContext;
}
-(void) tearDown
{
NSLog(@"BEGIN: ManagedObjectSenTestCase tearDown");
@try
{
self.context= nil;
self.model = nil;
self.coordinator = nil;
[pool release];
pool = nil;
}
@catch (NSException * e)
{
NSLog(@"%@",e);
NSLog(@"%@", [e callStackSymbols]);
NSLog(@"context reset failed!");
@throw(e);
}
NSLog(@"END: ManagedObjectSenTestCase tearDown");
}
This sample creates the core data stack and you can insert entities into the created context for testing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With