I had the same conundrum. I like to keep unit test coverage at 100% whenever possible. There is no easy way to generate an organic error condition. In fact, I'm not sure the current implementation of the 4 store types that come with Core Data will ever trigger an error in response to executeFetchRequest:error. But as it could happen in the future, here is what I did:
I have one unit test case file that is dedicated to validating how my classes handle errors populated by executeFetchRequest:error. I define a subclass of NSIncrementalStore that always produces an error during requests in the implementation file. [NSManagedObjectContext executeFetchRequest:error] is processed by [NSPersistentStoreCoordinator executeRequest:withContext:error:] which processes [NSPersistentStore executeRequest:withContext:error:] on all stores. You may notice that the word "fetch" drops when you move to the coordinator - saves and fetch requests are handled by the same method executeRequest:withContext:error:. So I get coverage for testing against save errors and fetch requests by defining a NSPersistentStore that will always respond to saves and fetches with errors.
#define kErrorProneStore @"ErrorProneStore"
@interface ErrorProneStore : NSIncrementalStore
@end
@implementation ErrorProneStore
- (BOOL)loadMetadata:(NSError **)error
{
//Required - Apple's documentation claims you can omit setting this, but I had memory allocation issues without it.
NSDictionary * metaData = @{NSStoreTypeKey : kErrorProneStore, NSStoreUUIDKey : @""};
[self setMetadata:metaData];
return YES;
}
-(void)populateError:(NSError **)error
{
if (error != NULL)
{
*error = [[NSError alloc] initWithDomain:NSCocoaErrorDomain
code:NSPersistentStoreOperationError
userInfo:nil];
}
}
- (id)executeRequest:(NSPersistentStoreRequest *)request
withContext:(NSManagedObjectContext *)context
error:(NSError **)error
{
[self populateError:error];
return nil;
}
- (NSIncrementalStoreNode *)newValuesForObjectWithID:(NSManagedObjectID *)objectID
withContext:(NSManagedObjectContext *)context
error:(NSError **)error
{
[self populateError:error];
return nil;
}
- (id)newValueForRelationship:(NSRelationshipDescription *)relationship
forObjectWithID:(NSManagedObjectID *)objectID
withContext:(NSManagedObjectContext *)context
error:(NSError **)error
{
[self populateError:error];
return nil;
}
- (NSArray *)obtainPermanentIDsForObjects:(NSArray *)array
error:(NSError **)error
{
[self populateError:error];
return nil;
}
@end
Now you can construct the Core Data stack using the ErrorProneStore and be guaranteed your fetch requests will return nil and populate the error parameter.
- (void)testFetchRequestErrorHandling
{
NSManagedObjectModel * model = [NSManagedObjectModel mergedModelFromBundles:nil];
[NSPersistentStoreCoordinator registerStoreClass:[ErrorProneStore class]
forStoreType:kErrorProneStore];
NSPersistentStoreCoordinator * coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
NSManagedObjectContext * context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[context setPersistentStoreCoordinator:coordinator];
[coordinator addPersistentStoreWithType:kErrorProneStore
configuration:nil
URL:nil
options:nil
error:nil];
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"AValidEntity"];
NSError * error;
[context executeFetchRequest:request
error:&error];
STAssertNotNil(error, @"Error should always be nil");
}