web-dev-qa-db-fra.com

Le nom de l'entité Core Data est nul lors de l'exécution d'un deuxième test unitaire

J'essaie d'ajouter des tests unitaires pour mon code Core Data. Mais j'ai toujours ce problème, le premier test s'exécute toujours correctement, mais le second se bloque car le nom de l'entité est nil. 

J'ai aussi cette erreur:

Multiple NSEntityDescriptions claim the NSManagedObject subclass 'Gym.Exercise' so +entity is unable to disambiguate.

Failed to find a unique match for an NSEntityDescription to a managed object subclass

Donc, je suppose que je ne fais pas quelque chose de bien dans tearDown().

override func setUp() {
    super.setUp()

    coreDataStack = CoreDataStack(storeType: .inMemory)
    context = coreDataStack.context
}

override func tearDown() {
    coreDataStack.reset()
    context = nil
    super.tearDown()
}

Voici ma classe CoreDataStack:

final class CoreDataStack {
    var storeType: StoreType!
    public init(storeType: StoreType) {
        self.storeType = storeType
    }

    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "Gym")
        container.loadPersistentStores { description, error in
            if let error = error {
                fatalError("Unresolved error \(error), \(error.localizedDescription)")
            } else {
                description.type = self.storeType.type

            }
        }

        return container
    }()

    public var context: NSManagedObjectContext {
        return persistentContainer.viewContext
    }

    public func reset() {
        guard let store = persistentContainer.persistentStoreCoordinator.persistentStores.first else { fatalError("No store found")}
        guard let url = store.url else { fatalError("No store URL found")}

        try! FileManager.default.removeItem(at: url)
        NSPersistentStoreCoordinator.destroyStoreAtURL(url: url)
    }
}

Et la définition de destroyStoreAtURL:

extension NSPersistentStoreCoordinator {
    public static func destroyStoreAtURL(url: URL) {
        do {
            let psc = self.init(managedObjectModel: NSManagedObjectModel())
            try psc.destroyPersistentStore(at: url, ofType: NSSQLiteStoreType, options: nil)
        } catch let e {
            print("failed to destroy persistent store at \(url)", e)
        }
    }
}

J'utilisais ce code dans le passé pour les tests unitaires et cela fonctionne, la différence est que par le passé, lorsque j'ai configuré les classes NSManagedObject dans l'éditeur, j'ai utilisé la configuration suivante:

Module - Global namespace
Codegen - Class Definition

Maintenant j'utilise:

Module - Current Product Module
Codegen - Manual/None

Parce que je veux ajouter mes classes manuellement. 

Alors, est-ce que quelqu'un sait pourquoi le comportement est différent maintenant?

edit - mon assistant d’extension NSManagedObject (l’erreur se produit dans la première ligne de la méthode fetch () lors d’une tentative de récupération du nom de l’entité):

extension Managed where Self: NSManagedObject {

    public static var entityName: String {
        return entity().name!
    }

    public static func fetch(in context: NSManagedObjectContext, configurationBlock: (NSFetchRequest<Self>) -> () = { _ in }) -> [Self] {
        let request = NSFetchRequest<Self>(entityName: Self.entityName)
        configurationBlock(request)
        return try! context.fetch(request)
    }

    public static func count(in context: NSManagedObjectContext, configure: (NSFetchRequest<Self>) -> () = { _ in }) -> Int {
        let request = NSFetchRequest<Self>(entityName: entityName)
        configure(request)
        return try! context.count(for: request)
    }

    public static func findOrFetch(in context: NSManagedObjectContext, matching predicate: NSPredicate) -> Self? {
        guard let object = materializeObject(in: context, matching: predicate) else {
        return fetch(in: context) { request in
                request.predicate = predicate
                request.returnsObjectsAsFaults = false
                request.fetchLimit = 1
            }.first
        }

        return object
    }

    public static func materializeObject(in context: NSManagedObjectContext, matching predicate: NSPredicate) -> Self? {
        for object in context.registeredObjects where !object.isFault {
           guard let result = object as? Self, predicate.evaluate(with: result) else {
               continue
           }

           return result
        }

        return nil
    }

    public static func findOrCreate(in context: NSManagedObjectContext, matching predicate: NSPredicate, configure: (Self) -> ()) -> Self {
        guard let object = findOrFetch(in: context, matching: predicate) else {
           let newObject: Self = context.insertObject()
           configure(newObject)
           return newObject
        }

        return object
    }
}
8
Kobe

Vous devriez essayer de supprimer toutes les instances trouvées de persistentStore au lieu de supprimer la première.

essayez de remplacer la fonction de réinitialisation par la suivante:

public func reset() {
    let stores = persistentContainer.persistentStoreCoordinator.persistentStores
    guard !stores.isEmpty else {
        fatalError("No store found")
    }
    stores.forEach { store in
        guard let url = store.url else { fatalError("No store URL found")}

        try! FileManager.default.removeItem(at: url)
        NSPersistentStoreCoordinator.destroyStoreAtURL(url: url)
    }
}

Et voyez si vous avez toujours le problème.

1
Bhaumik Desai