iOS: How to unit test Core Data?

Raul Peña Alonso
Apr 16 · 3 min read

Testing Core Data integration in your app is very useful and a lot easier than you think! In this post we show you how we create a test Core Data stack in Tiendeo and the basics for testing predicates, fetches, deletes and saves.

Create a test Core Data stack

We just need to set up the NSPersistentContainer as we usually do, but we don’t want to persist the data in disk but in-memory.

The in-memory data is cleaned as soon as the persistentContainer deinits and a new persistentContainer instance created will be start from zero, which is perfect for testing!

To achieve this, there are actually two options:

  • One option is to create a NSPersistentStoreDescription with type NSInMemoryStoreType and set this description to our persistentContainer, but with this set up certain features from SQLite storage like cascade deleting seems no to be available for in-memory storage, which maybe it’s ok for you.
  • The second option, and the one that Apple seems to recommend in this WWDC video, is to create a NSPersistentStoreDescription setting the url to /dev/null. This description informs the persistent container that it should write the data to /dev/null. Writing to /dev/null effectively uses an in-memory store, except you get all the features that you also get from the SQLite store that your app uses. This makes unit testing with a /dev/null based store far more accurate than an NSInMemoryStoreType based store.

The implementation should be like this:

This is the common implementation of a NSPersistentContainer but adding this NSPersistentStoreDescription to the container:

let description = NSPersistentStoreDescription()
description.url = URL(fileURLWithPath: "/dev/null")
...
container.persistentStoreDescriptions = [description]

and… our Core Data stack for testing is ready! Let’s move on and code our test.

Creating Core Data unit tests

Depending on the requirements of each project we can need to test Core Data fetches, predicates, updates, maybe migrations or performance of operations with a lot of content… but in most of the cases we’ll need to start our tests saving data in the data base.

Saving data to the data base is an asynchronous operation, so to manage asynchronous operations in a unit tests we use the expectation functionality which also offers the possibility of expect a certain notification from certain object.

Let’s work with this scenario to build our example: we have a data base with products and we want to test a simple fetch product by id.

In Tiendeo we write BDD-style Given/When/Then end-to-end tests, so let’s start with the Given part of the test where we’ll be saving different products to the data base.

The most relevant lines of this code snippet are:

  • Create a newBackgroundContext from our persistentContainer for testing:
let context = TestCoreDataStack().persistentContainer.newBackgroundContext()

I always recommend to perform Core Data operations on a background context, best practice is to perform data processing on a background queue.

  • Set up an expectation listening to .NSManagedObjectContextDidSave event from context:
expectation(forNotification: .NSManagedObjectContextDidSave, 
object: context) { _ in
return true
}
  • Once products are inserted and context executes save method, wait for expectation to receive the .NSManagedObjectContextDidSave event to continue with the execution.
waitForExpectations(timeout: 2.0) { error in
XCTAssertNil(error, "Save did not occur")
}

If everything have gone fine so far, the execution will go on with the When and Then parts:

Basically the When part executes a fetch with a predicate and the Then part checks if the data is the expected.

and… our first Core Data unit test is done!

In summary

In this post we’ve seen how to create a Core Data stack for testing. We’ve also seen the basics of almost every Core Data test which is how to insert data in a proper way.

With these basics, just changing the When condition in your tests you should be ready to test fetches, deletes, predicates and also performance of your implementation.

Hope you found this post interesting and useful for your projects. Any question or comment will be welcome!

Thanks and good luck!!!

Related articles

The following posts helped me out to code my current Core Data implementation in Tiendeo app and also for inspiration to write this post, they’re highly recommended:

Tiendeo Tech

Code, thoughts and solutions — www.tiendeo.com

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store