Upward Mobility: Unit Testing Core Data

There's no reason to leave your database-centric code untested

One of the more common issues that arises in creating OCUnit tests in iOS is how to test code that uses Core Data. There are several challenges, but with a little foresight, you can be sailing right along.

The first issue that most people encounter is getting access to the managed object context. If, like most people, you’ve followed the code snippets that Apple provides, or followed a tutorial on the web, you hung it off the AppDelegate, and use

to get a handle on the context when you need it. Alternatively, some people pass the context all the way down the view container chain, making it a property on each controller. I find this incredibly painful, and since most applications only have a single database instance open at once, there’s no reason not to keep the context around globally.

In any event, the problem with having your Core Data specific classes get the context from the sharedApplication is that when you run OCUnit tests, sharedApplication returns nil, and there’s no way to mock it. But, this is easily solved by creating a helper class that you use to access the context.

Now, whenever you need to get the context to make a Core Data call, you can just call

In your unit tests, you can have your setUp method instantiate the context and set the global variable testingContext to it. This brings us to the other challenge, creating the context. There are a few things you need to do in order to make it work. First, you need to make sure that your data model file is included in your OCUnit project. Second, you need to include the Core Data framework in the project. The last step is to create an empty, in-memory version of the database in your setUp method, which should look something like this:

In the above example, SomeClass should be a class that’s in your OCUnit project, and ModelName should be the filename of your Core Data object model. To be thorough, you might want to keep a handle to the store and coordinator, and call removePersistentStore in the tearDown method.

Now, you can write unit tests against your Core Data dependent classes, and they will seamlessly use either the live version of the database in the appDelegate, or your testing one, depending on whether the app is running live or you’re running unit tests.

 

Related

Sign up for the O'Reilly Programming Newsletter to get weekly insight from industry insiders.
topic: Programming
  • nathan187

    Hey Mr. Turner.

    Thanks for this example. If possible, is there an example project you can provide? I

  • DerekK19

    Admittedly I’m using XCode 5, but I found I had to use fileUrlWithpath, not UrlWithString. Also you had a type with momUrl (should have been modUrl). Otherwise, a nice elegant solution

  • gumbright

    An in memory store is not useful in a good many cases. For example, they do not work properly when trying to execute a fetch request of NSDictionaryReturnType. Just a heads up.

  • Pawel

    In my case NSURL *modURL = [NSURL URLWithString:path]; is nil. Testing in main bundle works but it’s not proper way to do it.

  • rydewnd2

    I like where you’re going with creating a wrapper for the core data stack. But why not pull all of this code out of the App Delegate and access VIA it’s own singleton? I think Apple’s template is quite antiquated.