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 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.
When I started researching about Core Data, I notice two things.
- 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.
- 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
Contextentities (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
Contextin 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)
Create new task / Update existing task
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
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
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,
Persistent Container to redirect it the memory and use that
subclass in the Unit Test class itself!”.
Here is the sub-class
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:
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
DatabaseManagerclass and redirect the
NSPersistentStoreCoordinatorto 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.
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