How should I test the findByAttribute
instance method I added to NSManagedObject
?
At first, I thought of programmatically creating an independent Core Data stack as demonstrated by Xcode's Core Data Utility Tutorial. And, in my search for that documentation, I came across Core Data Fetch Request Templates and thought that maybe instead of creating the method I made, I should make fetch request templates, but it doesn't look like the entityName
can be variable with a fetch request template, can it? Can I create a fetch request template on NSManagedObject
so that all subclasses can use it? Hmm, but then I would still need an entityName
and I don't think there's a way to dynamically get the name of the subclass that called the method.
Anyway, it looks like a good solution is to create an in-memory Core Data stack for testing, independent from the production Core Data stack. @Jeff Schilling also recommends creating an in-memory persistent store. Chris Hanson also creates a persistent store coordinator to unit test Core Data. This seems similar to how Rails has a separate database for testing. But, @iamleeg recommends removing the Core Data dependence.
Which do you think is the better approach? I personally prefer the latter.
UPDATE: I'm unit testing Core Data with OCHamcrest and Pivotal Lab's Cedar. In addition to writing the code below, I added NSManagedObject+Additions.m
and User.m
to the Spec
target.
#define HC_SHORTHAND
#import <Cedar-iPhone/SpecHelper.h>
#import <OCHamcrestIOS/OCHamcrestIOS.h>
#import "NSManagedObject+Additions.h"
#import "User.h"
SPEC_BEGIN(NSManagedObjectAdditionsSpec)
describe(@"NSManagedObject+Additions", ^{
__block NSManagedObjectContext *managedObjectContext;
beforeEach(^{
NSManagedObjectModel *managedObjectModel =
[NSManagedObjectModel mergedModelFromBundles:nil];
NSPersistentStoreCoordinator *persistentStoreCoordinator =
[[NSPersistentStoreCoordinator alloc]
initWithManagedObjectModel:managedObjectModel];
[persistentStoreCoordinator addPersistentStoreWithType:NSInMemoryStoreType
configuration:nil URL:nil options:nil error:NULL];
managedObjectContext = [[NSManagedObjectContext alloc] init];
managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator;
[persistentStoreCoordinator release];
});
it(@"finds first object by attribute value", ^{
// Create a user with an arbitrary Facebook user ID.
NSNumber *fbId = [[NSNumber alloc] initWithInteger:514417];
[[NSEntityDescription insertNewObjectForEntityForName:@"User"
inManagedObjectContext:managedObjectContext] setFbId:fbId];
[managedObjectContext save:nil];
NSNumber *fbIdFound = [(User *)[User findByAttribute:@"fbId" value:(id)fbId
entityName:@"User"
inManagedObjectContext:managedObjectContext] fbId];
assertThatInteger([fbId integerValue], equalToInteger([fbIdFound integerValue]));
[fbId release];
});
afterEach(^{
[managedObjectContext release];
});
});
SPEC_END
If you can tell me why if I don't cast to (id)
the fbId
argument passed to findByAttribute
I get
warning: incompatible Objective-C types 'struct NSNumber *',
expected 'struct NSString *' when passing argument 2 of
'findByAttribute:value:entityName:inManagedObjectContext:' from
distinct Objective-C type
then you get bonus points! :) It seems that I shouldn't have to cast an NSNumber
to an id
if the argument is supposed to be an id
because NSNumber
is an id
, right?