Escort Missions Logo

Escort Missions

You ever get the feeling that life is just one long series of Escort Missions?

NSInMemoryStoreType Is Not a Good Substitute in Unit Tests

So I was writing some tests for my KidChart app, which uses core data, and I wasted a ton of time, so I thought I’d post to warn people.  I wanted to avoid having to reset the state for each test, and I wanted the tests to run quickly, so I used NSInMemoryStoreType for my persistent store in my unit tests. This is a technique I’ve used before in other programming languages, and I was new to Core Data, so I was applying what I had done before to something I had insufficiently researched.

The app has a pretty simple data model.  There are Events, and each Event has a Child and a Behavior, and each Behavior has a good/bad score attached to it.  On my home screen, there’s a summary view, where I show the number of events for each child for each (good/bad) score for that day. I was thinking I could use an NSPredicate to grab just what I wanted.  Basically, what I wanted was:

1SELECT Child.Name , Behavior.Score ,count(Event. *)

2FROM Child , Behavior , Event

3WHERE = AND 4Event.Behavior_id = AND 5Event. date

‘Today at Midnight’ 6GROUP BY Child.Name , Behavior.Score

Except you can’t have direct SQL access.  So what I ended up with (after much iteration) was:

1NSFetchRequest request = [[NSFetchRequest alloc] init]; 2NSEntityDescription entity = [NSEntityDescription entityForName:@ “Child”

inManagedObjectContext:managedObjectContext ]; 4[request setEntity:entity ]; 5 6// Specify that the request should return dictionaries. 7[request setResultType:NSDictionaryResultType]; 8 9// Create an expression for the key path. 10NSExpression childEventKeyPathExpression = [NSExpression expressionForKeyPath: 11 @ “events”]; 12NSExpression onlyHappyEvents = [NSExpression expressionForSubquery: 13 childEventKeyPathExpression

usingIteratorVariable:@ “e”

predicate:[NSPredicate predicateWithFormat:@ “$e. 16behavior.score == 1 and $e.timeStamp > %@”

argumentArray:[NSArray arrayWithObject: currentDate ]]]; 18 19// Create an expression to represent the count value at the key path ‘events’ 20NSExpression *happyCountExpression = [NSExpression expressionForFunction:@ “count:”

arguments:[NSArray arrayWithObject: 22 onlyHappyEvents ]]; 23 24 25// Create an expression description using the minExpression and returning a date. 26NSExpressionDescription *happyCountExpressionDescription = [[NSExpressionDescription

alloc] init]; 28 29// The name is the key that will be used in the dictionary for the return value. 30[happyCountExpressionDescription setName:@ “happyEventCount”]; 31[happyCountExpressionDescription setExpression:happyCountExpression ]; 32[happyCountExpressionDescription setExpressionResultType:NSInteger16AttributeType]; 33 34NSExpression *onlySadEvents = [NSExpression expressionForSubquery: 35 childEventKeyPathExpression

usingIteratorVariable:@ “e”

predicate:[NSPredicate predicateWithFormat:@ “$e. 38behavior.score == -1 and $e.timeStamp > %@”

argumentArray:[NSArray arrayWithObject: currentDate ]]]; 40// Create an expression to represent the count value at the key path ‘events’ 41NSExpression *sadCountExpression = [NSExpression expressionForFunction:@ “count:”

arguments:[NSArray arrayWithObject:onlySadEvents ]] 43
; 44 45// Create an expression description using the minExpression and returning a date. 46NSExpressionDescription *sadCountExpressionDescription = [[NSExpressionDescription

alloc] init]; 48 49// The name is the key that will be used in the dictionary for the return value. 50[sadCountExpressionDescription setName:@ “sadEventCount”]; 51[sadCountExpressionDescription setExpression:sadCountExpression ]; 52[sadCountExpressionDescription setExpressionResultType:NSInteger16AttributeType];

53 54NSExpression childNameKeyPathExpression = [NSExpression expressionForKeyPath: 55 @ “Name”]; 56NSExpressionDescription nameExpressionDescription = [[NSExpressionDescription alloc] 57
init]; 58[nameExpressionDescription setName:@ “childName”]; 59[nameExpressionDescription setExpression:childNameKeyPathExpression ]; 60[nameExpressionDescription setExpressionResultType:NSStringAttributeType]; 61 62// Set the request’s properties to fetch just the property represented by the 63expressions. 64[request setPropertiesToFetch:[NSArray arrayWithObjects:nameExpressionDescription ,

65happyCountExpressionDescription , sadCountExpressionDescription , nil]]; 66

67// Execute the fetch. 68NSArray *objects = [managedObjectContext executeFetchRequest:request error:&error ]; 69STAssertNil(error , @ “error not nil:%@”,error );

Which is way more verbose than the corresponding SQL, but looks scarier than it actually is.  And it worked great.  And then, I changed my persistent store type to  NSSQLiteStoreType.  And it started throwing “ NSInvalidArgumentException: -predicate only defined for abstract class” errors. And I got to start all over.