iOS: How to unit test Core Data?
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 typeNSInMemoryStoreType
and set this description to ourpersistentContainer
, 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 anNSInMemoryStoreType
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 ourpersistentContainer
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 fromcontext
:
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: