New Core Data Stack (PersistenceController) in Unit Tests

Viewed 451

With the new Swift App Xcode template, I see the PersistenceController struct was added with a shared and preview instance to serve as the Core Data stack. From what I understand, the preview instance is for SwiftUI previews.

If I have a service class that takes an instance of my Core Data stack, should I be using the preview instance as part of my unit tests? Or should I create some sort of spy/fake to pass into the service class I am testing?

In general, what is the best practices for testing classes that use Core Data?

1 Answers

I am also just looking into this. I did not find a best practice. So I will summarize what I do. The PersistenceController is a nice pattern.

The static var preview allows you to create a different store and add some objects to it, like

  static var preview: PersistenceController = {
    let result = PersistenceController(inMemory: true)
    let viewContext = result.container.viewContext
    // this is the part where you can add mock data
    for _ in 0..<10 {
        let newItem = Note(context: viewContext)
        newItem.creationDate = Date()
    }
    do {
        try viewContext.save()
    } catch {
        let nsError = error as NSError
        fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
    }
    return result
}()

For unit tests we would want to start with an empty store, so I just added one more var in PersistenceController

static let unitTest: PersistenceController = {
    let result = PersistenceController(inMemory: true)
    //empty store
    return result
}()

This makes my unit test look like

class TestCoreDataStack: XCTestCase {

var coreDataStack: PersistenceController!

override func setUp() {
    super.setUp()
    coreDataStack = PersistenceController.unitTest
}

override func tearDown() {
  super.tearDown()
   //called after the invocation of each test method in the class.
   // I am deleted all notes to start fresh for the next test
  UnitTestHelpers.deleteAllNotes(container: coreDataStack.container)  
  coreDataStack = nil
}

func testAddNote() {
    let context = coreDataStack.container.viewContext
    
    let title = "my new title"
    let note = Note(title: title, context: context)
    
    XCTAssertNotNil(note, "note should not be nil")
    XCTAssertTrue(note.title == title)
    XCTAssertNotNil(note.creationDate, "notes date should not be nil")

} }
Related