Unit Testing Core Data Models

Setting Up the Project

Unit Testing Core Data Models
1 Setting Up the Project 04:11
2 Setting Up the Core Data Stack Plus 07:02
3 Unit Testing a Core Data Model Plus 05:59
4 Writing Better Unit Tests Plus 11:19
5 Writing More Unit Tests Plus 05:06

In this series, I show you how to unit test Core Data models. Before we start, though, I’d like to challenge you. How would you unit test a Core Data model? Pause the video for a moment and try to come up with a strategy. Remember what we covered in Core Data Fundamentals. Which ingredients do we need to create an NSManagedObject instance?

Revisiting Notes

In Core Data Fundamentals, we built an application, Notes. The data model of Notes defines three entities, Note, Category, and Tag. In this episode, we unit test the Note and Category entities. Open the starter project of this episode in Xcode and take a look at the data model to freshen up your mind.

The Data Model of Notes
The Data Model of Notes

What to Unit Test

It’s important to understand what we need to unit test. For example, there’s no need to unit test the attributes and relationships of the entities. Why is that? It’s not our task to unit test the Core Data framework. Apple has already taken care of that for us.

What we need to unit test is the custom behavior we added to the NSManagedObject subclasses Xcode generated for us. That’s why we only need to unit test the Note and Category classes. We didn’t add any custom behavior to the Tag class.

The custom behavior we added to the Note and Category classes is located in Note.swift and Category.swift in the Core Data > Extensions group. We created extensions for the Note and Category classes to add custom behavior to these NSManagedObject subclasses. This is what the extensions look like.

Note.swift

import Foundation

extension Note {

    // MARK: - Dates

    var updatedAtAsDate: Date {
        return updatedAt ?? Date()
    }

    var createdAtAsDate: Date {
        return createdAt ?? Date()
    }

    // MARK: - Tags

    var alphabetizedTags: [Tag]? {
        guard let tags = tags as? Set<Tag> else {
            return nil
        }

        return tags.sorted(by: {
            guard let tag0 = $0.name else { return true }
            guard let tag1 = $1.name else { return true }
            return tag0 < tag1
        })
    }

    var alphabetizedTagsAsString: String? {
        guard let tags = alphabetizedTags, tags.count > 0 else {
            return nil
        }

        let names = tags.flatMap { $0.name }
        return names.joined(separator: ", ")
    }

}

Category.swift

import UIKit

extension Category {

    var color: UIColor? {
        get {
            guard let hex = colorAsHex else { return nil }
            return UIColor(hex: hex)
        }

        set(newColor) {
            if let newColor = newColor {
                colorAsHex = newColor.toHex
            }
        }
    }

}

The goal of this episode is to unit test the computed properties defined in these extensions. Before we can do that, we need to create a unit test target. Let’s do that first.

Adding a Unit Test Target

Select the project in the Project Navigator, click the plus button at the bottom, and choose iOS Unit Testing Bundle.

Adding a Unit Test Target
Adding a Unit Test Target
Choose iOS Unit Testing Bundle
Choose iOS Unit Testing Bundle

The defaults are just fine. Make sure Language is set to Swift and Target to be Tested is set to Notes.

Configuring the Unit Test Target
Configuring the Unit Test Target

The Notes project should now have two targets, Notes and NotesTests.

Adding a Unit Test Target
Adding a Unit Test Target

Organizing the Unit Testing Bundle

Before we continue, I’d like to organize the unit testing bundle to keep files and folders nice and tidy. I usually create four groups, Supporting Files for the Info.plist file, a group for stubs, a group for extensions, and a group for the test cases. This is what you should end up with. Notice that I also removed the test case Xcode created for us, NotesTests.swift. We won’t be needing it.

Organizing the Unit Testing Bundle
Organizing the Unit Testing Bundle

If you take this approach and move the Info.plist file into the Supporting Files group, make sure you tell Xcode about this change. It means we need to update the path to the Info.plist file in the build settings of the NotesTests target.

Choose the NotesTests target from the list of targets, select Build Settings at the top, and search for the Info.plist File build setting in the Packaging section. Change the path from NotesTests/Info.plist to NotesTests/Supporting Files/Info.plist.

Updating the Build Settings
Updating the Build Settings

Let’s run the test suite to make sure the unit test target is correctly configured. We don’t have any unit tests yet, but that isn’t a problem. We only want to verify that the unit test target is properly configured.

Choose a simulator from the list of devices and run the test suite by choosing Test from Xcode’s Product menu. Xcode installs the application in the simulator and runs the test suite. No errors or warnings should be visible.

What’s Next

Remember from Core Data Fundamentals that we need a managed object context to instantiate a managed object. This implies that we need access to a Core Data stack. In the next episode, we set up the Core Data stack.

Next Episode "Setting Up the Core Data Stack"

Stop Writing Swift That Sucks

Download the Swift Patterns I Swear By