Search code examples
swiftunit-testingcore-dataswift-package-manager

How to expose CoreData to swift package unit tests?


I'm trying to test CoreData in my swift package as SPM now supports bundled resources including .xcdatamodel, my tests can't seem to locate my NSManagedObjects though. What are the steps to unit test core data from the tests?

I'm getting this error when i try to create a NSManagedObject from a test:

+entityForName: could not locate an entity named 'StriveUser' in this model. (NSInternalInconsistencyException)

I've triple checked the naming and it's all correct.

I'm creating the object like this from my tests:

let object = NSEntityDescription.insertNewObject(forEntityName: "StriveUser", into: self.inMemoryStore.context)

And here's my code for locating the .xcdatamodel:

    fileprivate var managedObjectModel: NSManagedObjectModel = {
    guard let managedObjectModel = NSManagedObjectModel.mergedModel(from: [Bundle.main]) else {
        preconditionFailure("Error getting ManagedObjectModel")
    }

    return managedObjectModel
}()

final class InMemoryStore {
    let context: NSManagedObjectContext

    init() {
        let description = NSPersistentStoreDescription()
        description.type = NSInMemoryStoreType
        description.shouldAddStoreAsynchronously = false

        let container = NSPersistentContainer(name: Constants.modelName, managedObjectModel: managedObjectModel)
        container.persistentStoreDescriptions = [description]
        container.loadPersistentStores {_, error in
            if let error = error {
                fatalError("Failed to load store: \(error.localizedDescription)")
            }
        }

        self.context = container.viewContext
    }
}

Solution

  • If you declare a Swift tools version of 5.3 or later in your package manifest, you can bundle resources with your source code as Swift packages. For example, Swift packages can contain asset catalogs, storyboards, and so on.

    When resources are defined, a new static Bundle reference is created for the package. This can be accessed using Bundle.module.

    So for your ManagedObjectModel you will need update the bundle reference. A good way of using this is to have an accessor in your package that will return the model.

    For additional information, you can check out Apple's developer documentation Bundling Resources with a Swift Package.