Core Data Basics: Testing
Yeah, testing is important
- You will be expected to be aware how to make a Single View Application in Swift
- You’ll need to know about the basics of Core Data
- You’ll find this easier if you know something about Dependency Injection and Protocols, but of course you can read on and find how they are used in this project
- This article does use a custom alertview, but if you want a full guide to that I’ve written just that here.
Core Data: A framework that allows you to manage the model layer objects in your application. Core Data does this by being an object graph management and persistence framework.
You should be testing! Right? TDD has gone from something reserved for the cool kids to something that you should be doing in your personal projects (I wonder if I’ll get comments from the Wrong Brigade about this? It’s a pity comment, person I’m imagining)
By storing Core Data files there are difficulties in isolating the database from the tests. Each test should be repeatable and it would be quite heavyweight to delete the whole database after every test.
The example App
I created an App that represents a rather simple ToDo App. The implementation is not all that great — I mean it’s using MVC for a start, and the less said about the functionality, the better.
Still, this is here to demonstrate *testing* core data. Can it be done, and can we use the principes of Dependency Injection?. SPOILER: You’re reading this, so it can!
When you create a new project you have the option to add
core data to your fledgling project. Now certainly with many new things to cope with, like sceneDelegate — I’d advise you to tick that box!
This gives the
persistent container in the
App Delegate as well as a rather nice
xcdatamodeld model for you to use nestled in your project.
The App setup: make it testable with a protocol
protocol looks like the following:
class (and in this example it will be a
class) that conforms to this
protocol must be able to return the tasks as an array of
NSManagedObject, and be able to save a particular task represented by a
String, as well as having a visible initialiser.
Note: It is temping to use a `
Singleton` instance of a core data manager here. However this is *not* the correct strategy as there is only a single instance of the core data stack this is not necessary. Apple’s documentation covers passing around a managed object context, and I recommend you too follow this approach in your software.
This has a rather spectacularly uneventful initialiser. However,
getTasks provides us with an array of managed objects — but has cached any previous fetch (excuse the lack of error handling here), and save indeed does save using the
func save(task: String) function to our single context. On to the code!
The core of this
UIViewController instance is about using the core manager (or anything that conforms to that handy
CoreDataManagerProtocol) to populate the
UITableView — and here if the
CoreDataManagerProtocol doesn’t return us any tasks then we display a placeholder (note to self: Would be nice to make this animated, wouldn’t it?)
Now my method for this particular article is to add the dependencies through dependency injection, here not even using
Storyboards (sorry!). I’m not particularly proud of this code, but the following does the job:
The setup for the tests
Testing in Xcode is lovely, or something. This means that I always need to remind myself to add
@testable import CoreDataToDoTesting at the top of my test class to give visibility of the main App code.
So within the test case class will need to have it’s own
NSManagedObjectModel` (if you’ll excuse the force-unwrapping for tests):
Now our tests should be repeatable — that means for each test in the
tearDown() we need to get rid of anything that is in the
NSPersistentStoreCoordinator, and that can be done in code using the following:
we then set up the
NSPersistentStoreCoordinator and the
NSManagedObjectModel which is going to be the same for each and every test
Now the intention around this project is to inject a mock. So, in steps the
fantastic! We can now more onto the actual tests themselves!
These tests are not intended to be production read, but rather give an indication about how testing can be performed for a core data instance.
In theory, what we are able to do is swap out the
CoreDataManager with our mocked version and check to see whether the view controller responds how it is expected to do so.
Get the task from our mocked CoreDataManager
Here the viewcontroller uses the moccked
CoreDataManager. Now I’m no fan of checking the
UITableViewCell as this is a user interface element rather than something that should usually be Unit Tested, however this *does* allow us to see whether the first element is displayed from the mock (this is the assertion).
Get a saved task from our mocked CoreDataManager
This is actually arguably testing the mocked
CoreDataManager — but in this case I am reasonably certain that the View Controller instance does display the contents of the
Use the production CoreDataManager — but inject an entity
This approach is not recommended, but it gives you the cance to see how you can inject an entity into the
CoreDataManager. This is not great to use as we are using the real
CoreDataManager — so this is dependent on the state of the actual device you are testing on — and this essentially isn’t the correct approach to testing.
Can we also test fetching and saving?
Testing and using Dependency Injection only really makes sense with concrete examples.
Equally if we want to test our Core Data implementation it is a good idea to use concrete examples.
This article had given you just this!
I hope this article has been of help to you, and you can see the code from this is included in the attached Repo to get the whole lot in one go!
If you’ve any questions, comments or suggestions please hit me up on Twitter