Core Data Wrapper Class, Mocking and Unit Testing in Swift 3!

Another - team - coursework is over, now I will describe a different story. Focusing on database-driven iOS App using Core Data. I will describe how Core Data can be implemented and how you can create a mock Core Data class to unit test Core Data.

Today’s Objectives

Today we are focusing Core Data; iOS’s way to support database-driven apps. In detail:

  • Go through our story and problems we had with Core Data and Swift 3.
  • Show how important wrapping class can be for more readable and flexible code.
  • Show how easy is to mock core data class.
  • Show how you can Unit Test your Core Data class using the mock version of Core Data.

Initial problem

When I started researching about Core Data, I notice two things.

  1. The code required to use Core Data in a class was an overhead. Think that we had approximately 10 classes for our app, that all of them needed access to the database. It would be a pain to have this code all over the place. Would not be flexible.
  2. Secondly, CoreData at the time this article was written was not up-to-date with Swift, there was Objective-C code mixed etc.

We thought we should introduce a wrapping class. A class that will wrap everything related to Core Data and expose this class as an internal API to be used in our app.

The benefits are readability, flexibility, easily testable and Single Responsibility Principle.

The DatabaseManager.swift file

Before, listing the code, I would like to introduce you to the concept of our app so that you can understand the code later:

  • The code below is not the entire code used in the project, I removed some methods to left with a minimal example.
  • In our case, we have Task and Context entities (think of them as tables in the database). Task is self-explanatory, and Context is just anything like a place, person or even tool or equipment that the task can be associated with it.
  • A property of our design is to be able to find all tasks given a specific Context instance.
  • I left both Task and Context in the DatabaseManager just to demonstrate some of the polymorphic methods implemented.

So now if we need to do database operations in our app is quite easy:

Get all tasks (Select all)

DatabaseManager.instance.allTasks
DatabaseManager.instance.allContexts

Create new task / Update existing task

let newTask = DatabaseManager.instance.getEmptyTaskInstance
newTask.title = "Some title"
DatabaseManager.instance.saveOrUpdate(instance: newTask)

The idea is that you provide the object to be added or updated and CoreData will decide if is an update or add operation. Note that we don’t have to specify method per class, we use the superclass NSManagedObject which all entities inherit.

Delete task

DatabaseManager.instance.delete(instance: someTaskInstance)

Mocking CoreData and Unit Testing

We wanted to unit test DatabaseManager.swift but we had a problem! We couldn’t do proper Unit Test since the DatabaseManager depends on external entities like the database file! Not only the file could be inaccessible or not available, but also database has a state, which is not something we want in Unit Testing.

We want every test to start with empty, yet clean database and we don’t want tests to prepare or set the conditions for the next tests, because if any of the tests fail we will have a domino-effect causing most of the tests to fail as well!

How can we deal with that? Well, I found a really interesting way that redirects the Persistent Container of Core Data from the actual SQLite file to the device memory! (Hauser, 2015)

When I saw that, I said “Aha! Let’s just subclass the DatabaseManager, override the Persistent Container to redirect it the memory and use that subclass in the Unit Test class itself!”.

Here is the sub-class MockDatabaseManager.swift:

Pay attention to line 58. Is where we redirect the Persistent Store to memory instead of the SQLite file.

So now we can unit test the DatabaseManager:

Here is a very minimal example:

That’s it!

Today we have seen:

  • How we can wrap CoreData code into a nice Swift class and expose an API to be used in the entire project.
  • How we can sub-class DatabaseManager class and redirect the NSPersistentStoreCoordinator to the memory instead of the SQLite file so that we can create a Mock version of the CoreData class
  • How to Unit Test the mock version of the database.

I had a really hard time finding out this information, and I decided to share my experience, code and knowledge on this topic with the hope that I will help someone working with Swift 3 and Core Data.
Rafael

References

Hauser D. 2015. Unit Testing Tutorial: Mocking Objects. [Online]. [Accessed 06 December 2016]. Available from: https://www.raywenderlich.com/101306/unit-testing-tutorial-mocking-objects

© 2018 Rafael Papallas
Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.