Unit Testing Model Layer with Core Data and Swift

Updated on November 19, 2015 – Swift 2.0

As I approached testing my Core Data model, I have to admit I was apprehensive. How in the world was I going to write unit tests for my model layer that depended on a database. Past experience with trying to write tests with databases was painful. I feared the same would be the case with Core Data.

To my surprise, unit testing my Core Data model layer has been… well… amazing. With little effort, I’ve been able to write the unit tests I’ve wanted. The process went something like this for me:

  1. Create an NSManagedObject subclass of the Core Data entity that I need in my unit test. (This just makes things easier from an Xcode code-completion standpoint)
  2. Write a helper function to set up an in-memory NSManagedObjectContext. Avoiding the need to use an actual sqlite database is pretty handy. It allows for quick-running tests and easy iterations over the data model itself.
  3. Write unit tests using the in-memory NSManagedObjectContext.

I’ve already written about creating an NSManagedObject subclass, so I will unpack steps 2 and 3 in this blog entry.

Set up an in-memory NSManagedObjectContext

A Stack Overflow question+answer sparked some thoughts. The idea and the code both came from there. The answer uses Objective-C, so my contribution is that I’ve written it in Swift. In my project, I created a new Swift file called “CoreDataHelpers.swift” in my tests target. Here’s a look at the helper function:

 1import CoreData
 2
 3func setUpInMemoryManagedObjectContext() -> NSManagedObjectContext {
 4    let managedObjectModel = NSManagedObjectModel.mergedModelFromBundles([NSBundle.mainBundle()])!
 5    
 6    let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
 7
 8    do {
 9        try persistentStoreCoordinator.addPersistentStoreWithType(NSInMemoryStoreType, configuration: nil, url: nil, options: nil)
10    } catch {
11        print("Adding in-memory persistent store failed")
12    }
13    
14    let managedObjectContext = NSManagedObjectContext()
15    managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator
16    
17    return managedObjectContext
18}

Observations

I’ll be honest, I’m only starting to put together the pieces involved in setting up the Core Data stack. Working through these unit testing techniques has solidified a lot. Analyzing the helper method from the bottom up has made some sense out of how to configure everything:

  • I need an NSManagedObjectContext whose NSPersistentStoreCoordinator property uses an in-memory store.
  • To get such an NSManagedObjectContext, I need to add a persistent store with a type of NSInMemoryStoreType to an NSPersistentStoreCoordinator instance. (note the line that’s highlighted)
  • Of course, in order to do that, I need an NSPersistentStoreCoordinator instance, and I can’t get one of those unless I initialize it with an NSManagedObjectModel.
  • To get an NSManagedObjectModel, I use the class method, mergedModelFromBundles() to grab it from my main bundle.
  • Fast-forwarding now: With a proper NSManagedObjectModel instance, I can create an NSPersistentStoreCoordinator instance with it and add an NSInMemoryStoreType to that persistentStoreCoordinator. Finally, I can initialize an NSManagedObjectContext, assign the configured persistentStoreCoordinator to the context, and return it.

Whew! This whole process felt a lot like reading If You Give a Mouse a Cookie, but that may be because I’ve read it a few hundred times to my 2 year old. :]

Writing the unit test(s)

With the ability to get an NSManagedObjectContext instance that’s using an in-memory store, the unit tests using Entities from my Core Data model are quite easy.

Here’s a sample test:

 1import CoreData
 2
 3class TestsUsingModelFromCoreData: XCTestCase {
 4    func testSomethingUsingCoreData() {
 5        let managedObjectContext = setUpInMemoryManagedObjectContext()
 6        let entity = NSEntityDescription.insertNewObjectForEntityForName("EntityName", inManagedObjectContext: managedObjectContext)
 7        
 8        // model setup
 9        
10        // XCTAssert    
11    }
12}

Conclusion

I was so surprised at how straightforward the test was. The helper function makes a world of difference for me. I hope it does for you, too!

You might also enjoy…

comments powered by Disqus