Core Data Basics: Testing
By: Steven Curtis
Tags: Swift Programming, Software Development, Swift

Core Data Basics: Testing

Yeah, testing is important

Photo by Andriyko Podilnyk on Unsplash

Prerequisites:

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.

Terminology

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.

Why test

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!

The setup

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

This has often been my mantra — use DI through the use of Protocols. In fact this approach mirrors my Network Manager

The protocol looks like the following:

Any 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.

The CoreDataManager

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.

Therefore…

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 ViewController

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 NSPersistentStoreCoordinator , NSManagedObjectModel and 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 CoreDataManagerMock instance

fantastic! We can now more onto the actual tests themselves!

The tests

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 CoreDataManager correctly.

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?

Conclusion

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