Give It a Try

It's time to shift gears and focus on the Category class. This shouldn't be difficult since you already know how to write unit tests for a Core Data model and we laid the foundation when we implemented the unit tests for the Note class. Take a few minutes to give it a try.

Let's take a look at the extension of the Category class to get an idea of what needs to be unit tested. We want to unit test the getter and setter of the color computed property. We start, as always, by creating a new XCTestCase subclass, CategoryTests.swift. Add an import statement for the Core Data framework and the Notes module.

CategoryTests.swift

import XCTest
import CoreData
@testable import Notes

class CategoryTests: XCTestCase {

}

We define a private, variable property, managedObjectContext, of type NSManagedObjectContext!, an implicitly unwrapped optional. This should look familiar.

CategoryTests.swift

import XCTest
import CoreData
@testable import Notes

class CategoryTests: XCTestCase {

    // MARK: - Properties

    private var managedObjectContext: NSManagedObjectContext!

}

The setUp() and tearDown() methods are identical to the ones of the NoteTests class. We could create a XCTestCase subclass which NoteTests and CategoryTests inherit from, but that's an optimization I won't cover in this episode.

CategoryTests.swift

import XCTest
import CoreData
@testable import Notes

class CategoryTests: XCTestCase {

    // MARK: - Properties

    private var managedObjectContext: NSManagedObjectContext!

    // MARK: - Set Up & Tear Down

    override func setUp() {
        super.setUp()

        // Create Managed Object Context
        managedObjectContext = setupCoreDataStack(with: "Notes", in: Bundle.main)
    }

    override func tearDown() {
        super.tearDown()

        // Clean Up
        managedObjectContext = nil
    }

}

We also define several private constant properties, which we use in the unit tests. I prefer this approach over literals scattered across the unit tests. That's a personal choice.

CategoryTests.swift

import XCTest
import CoreData
@testable import Notes

class CategoryTests: XCTestCase {

    // MARK: - Properties

    private var managedObjectContext: NSManagedObjectContext!

    // MARK: -

    private let redColorAsHex = "FF5E5CFF"
    private let redColor = UIColor(red:1.00, green:0.37, blue:0.36, alpha:1.0)

    private let blueColorAsHex = "40B8D9FF"
    private let blueColor = UIColor(red:0.25, green:0.72, blue:0.85, alpha:1.0)

    ...

}

Before we write the first unit test, delete the unit tests Xcode created for us. We won't be needing those.

Unit Testing the Getter

To fully cover the getter of the color computed property, we need to implement two unit tests. We name the first unit test testColor_Getter_NotNil(). It tests the scenario in which the colorAsHex property isn't equal to nil.

We create a Category instance and set the value of its colorAsHex property to the value stored in the redColorAsHex constant. We assert that the value returned by the color computed property is not equal to nil and that the value returned is equal to the color we expect.

CategoryTests.swift

// MARK: - Tests for Color

func testColor_Getter_NotNil() {
    // Create Category
    let category = Category(context: managedObjectContext)

    // Configure Category
    category.colorAsHex = redColorAsHex

    // Assertions
    XCTAssertNotNil(category.color)
    XCTAssertEqual(category.color!.toHex, redColorAsHex)
}

It's important to emphasize that we unit test the color computed property. We're not unit testing any of the helper methods we implemented in the extension of the UIColor class. That's why we use the constant properties in the unit tests.

We're not done yet. I'd like to set the colorAsHex property to another value, the value stored in the blueColorAsHex constant, and perform the same assertions. Why do we do this?

CategoryTests.swift

// MARK: - Tests for Color

func testColor_Getter_NotNil() {
    // Create Category
    let category = Category(context: managedObjectContext)

    // Configure Category
    category.colorAsHex = redColorAsHex

    // Assertions
    XCTAssertNotNil(category.color)
    XCTAssertEqual(category.color!.toHex, redColorAsHex)

    // Invoke Method to Test
    category.colorAsHex = blueColorAsHex

    // Assertions
    XCTAssertNotNil(category.color)
    XCTAssertEqual(category.color!.toHex, blueColorAsHex)
}

By unit testing the getter of the color computed property with more than one color, we make the unit test more robust. It occasionally happens that a unit test passes because of a lucky coincidence and that's something we want to avoid.

The second unit test covers the scenario in which colorAsHex is equal to nil. We name the unit test testColor_Getter_Nil(). We create another Category instance and set its colorAsHex property to nil. In this scenario, the value returned by the color computed property should be equal to nil.

CategoryTests.swift

func testColor_Getter_Nil() {
    // Create Category
    let category = Category(context: managedObjectContext)

    // Configure Category
    category.colorAsHex = nil

    // Assertions
    XCTAssertNil(category.color)
}

Run the test suite to make sure the unit tests pass. Xcode's code coverage shows us that the getter of the color computed property is covered by unit tests.

Code Coverage in Xcode

Unit Testing the Setter

The unit test for the setter of the color computed property is also easy to implement. We create a Category instance, set the value of color, and assert that the value of the colorAsHex property is equal to the value we expect. We repeat this two times to make sure we weren't lucky with the values we've chosen. We don't want to fool ourselves.

CategoryTests.swift

func testColor_Setter() {
    // Create Category
    let category = Category(context: managedObjectContext)

    // Invoke Method to Test
    category.color = redColor

    // Assertions
    XCTAssertEqual(category.colorAsHex, redColorAsHex)

    // Invoke Method to Test
    category.color = blueColor

    // Assertions
    XCTAssertEqual(category.colorAsHex, blueColorAsHex)
}

Run the test suite one more time and take a look at the code coverage of the extension for the Category class. The extension is now fully covered by unit tests.

Code Coverage in Xcode

Wrapping Up

I hope this series has shown you that writing unit tests for Core Data models isn't that difficult. Once you've laid a proper foundation, it's pretty simple to put the Core Data models of your project to the test.

Support for code coverage is a welcome addition to Xcode and I find it very useful. But I hope that this series has shown you that code coverage is nothing more than a tool that helps you make decisions. Don't see the code coverage results as the absolute truth. Be critical and analyze the results carefully to make sure the unit tests you write make sense and properly unit test the code you write.