Swift ne peut pas tester les données de base dans les tests Xcode?

je travaille sur un projet iOS qui utilise des données de base. Je suis à l'utilisation de swift. La pile de données du noyau est configurée correctement et tout semble aller bien. J'ai créé une classe pour une entité (NSManagedObject) appelée TestEntity. La classe ressemble à ceci:

import UIKit
import CoreData

class TestEntity: NSManagedObject {
    @NSManaged var name: NSString
    @NSManaged var age: NSNumber
}

alors, j'essaie d'insérer une nouvelle identité de test dans le code en utilisant cette ligne de code:

let te: TestEntity = NSEntityDescription.insertNewObjectForEntityForName("TestEntity", inManagedObjectContext: ctx) as TestEntity

je reçois alors cette erreur:

enter image description here

j'ai vu quelques réponses sur le débordement de pile qui disent que je dois m'inquiéter du nom du module. Alors j'ai regardé que sur les docs: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WritingSwiftClassesWithObjective-CBehavior.html

puis je suis allé dans l'entité de données de base pour TestEntity et dans le champ de classe j'ai entré myAppName.TestEntity

Quand je lance l'application de cette ligne:

let te: TestEntity = NSEntityDescription.insertNewObjectForEntityForName("TestEntity", inManagedObjectContext: ctx) as TestEntity

me donne toujours la même erreur.

Quoi d'autre pourrais-je fait de mal?

modifier: Donc, j'ai pu faire en sorte que l'application ne se plante plus en changeant la classe de NSManagedObject de Testicentity pour: import UIKit import CoreData

@objc(TestEntity) class TestEntity: NSManagedObject {
    @NSManaged var name: NSString
    @NSManaged var age: NSNumber
}

donc, j'y ai ajouté le @objc (testicule). Cela fonctionne avec ou sans ajouter le nom d'application avant le nom de classe de Testitentity dans le core data model inspector.

Cela fonctionne, mais, quand je lance tests cette ligne est encore plantages:

let te: TestEntity = NSEntityDescription.insertNewObjectForEntityForName("TestEntity", inManagedObjectContext: ctx) as TestEntity

Donc, je trouve que c'est un problème pour d'autres personnes: comment accéder aux classes Obj-C générées par les données de base dans les cibles de test?

Comment Pouvons-nous obtenir des données de base pour fonctionner dans les tests de swift. Je n'utilise pas d'en-tête passerelle dans la cible app et tout fonctionne très bien. La cible de test craque toujours.

Comment puis-je fixer la cible de test pour qu'elle puisse exécuter des tests de données de base?

16
demandé sur Cœur 2014-07-09 23:59:50

5 réponses

avec Xcode 7, et @testable, Vous ne devriez plus avoir besoin de mettre à jour le managedObjectClassName ou utiliser d'autres hacks. Voici ce que j'ai fait pour le faire fonctionner en Xcode 7.2.

  1. Définissez votre application hôte cible de test et cochez "autoriser la mise à l'essai des applications hôtes API".

enter image description here

  1. assurez-vous que aucun de vos cours réguliers ont un membre cible pointant vers la cible de Test. Uniquement les classes à essai unitaire le code doit être réglé sur la cible D'essai.

enter image description here

  1. Ajouter @testable ligne vers le haut de toutes vos classes de test:
import XCTest
@testable import MyApp

class MyAppTests: XCTestCase {
}

Si vous avez encore des questions, vous pouvez essayer ces conseils supplémentaires: https://forums.developer.apple.com/message/28773#28949

je me suis battu avec celui-ci pendant un certain temps donc j'espère que ça aidera quelqu'un d'autre.

19
répondu johnford 2015-12-18 06:53:40

c'est parce que le cadre CoreData est toujours dans Objective-C. Swift utilise namespaced-classes, donc pour CoreData pour trouver vos classes swift vous devez spécifier le nom de la classe avec son namespace comme ceci:

enter image description here

le problème est que votre application n'a pas le même namespace que lorsque vous exécutez vos tests. <AppName>.<ClassName> et <AppName>Tests.<ClassName>

EDIT: Solution pour exécuter en tant qu'Application et les Tests

je viens d'écrire un morceau de code pour résoudre le <AppName>.<ClassName> et <AppName>Tests.<ClassName> problème. La solution que j'utilise actuellement (Xcode 6.1) est de ne pas remplir le Class champ dans L'UI de CoreData (montré ci-dessus), et de le faire dans le code à la place.

ce code détectera si vous êtes en cours D'exécution en tant que Tests App vs et utilisez le bon nom de module et mettre à jour le managedObjectClassName.

lazy var managedObjectModel: NSManagedObjectModel = {
    // The managed object model for the application. This property is not optional...
    let modelURL = NSBundle.mainBundle().URLForResource("Streak", withExtension: "momd")!
    let managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)!

    // Check if we are running as test or not
    let environment = NSProcessInfo.processInfo().environment as [String : AnyObject]
    let isTest = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"

    // Create the module name
    let moduleName = (isTest) ? "StreakTests" : "Streak"

    // Create a new managed object model with updated entity class names
    var newEntities = [] as [NSEntityDescription]
    for (_, entity) in enumerate(managedObjectModel.entities) {
        let newEntity = entity.copy() as NSEntityDescription
        newEntity.managedObjectClassName = "\(moduleName).\(entity.name)"
        newEntities.append(newEntity)
    }
    let newManagedObjectModel = NSManagedObjectModel()
    newManagedObjectModel.entities = newEntities

    return newManagedObjectModel
}()
6
répondu Ludovic Landry 2014-11-07 08:00:07

je pense que j'obtiens des résultats similaires à vous. Je n'ai pu obtenir mes tests de travail avec la ligne

var newDept = NSEntityDescription.insertNewObjectForEntityForName("Department", inManagedObjectContext: moc) as Department

Mais j'ai pu obtenir les tests en cours d'exécution avec :

let entity = NSEntityDescription.entityForName("Department", inManagedObjectContext: moc)
let department = Department(entity: entity!, insertIntoManagedObjectContext: moc)

Mon Entité ressemble à :

@objc(Department)
class Department: NSManagedObject {

    @NSManaged var department_description: String
    ...
}
5
répondu Oliver Shaw 2014-10-18 20:37:32

L'exemple de code de Ludovic ne couvre pas les sousentités. Ainsi, lors de la définition D'une entité mère dans CoreData, l'application se bloque.

adapté le code pour tenir compte des sousentités:

private func createManagedObjectModel() {

    // Get module name
    var moduleName: String = "ModuleName"
    let environment = NSProcessInfo.processInfo().environment as! [String : AnyObject]
    let isTest = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"
    if isTest { moduleName = "ModuleNameTests" }

    // Get model
    let modelURL = NSBundle.mainBundle().URLForResource(self.storeName, withExtension: "momd")!
    let model = NSManagedObjectModel(contentsOfURL: modelURL)!

    // Create entity copies
    var newEntities = [NSEntityDescription]()
    for (_, entity) in enumerate(model.entities) {
        let newEntity = entity.copy() as! NSEntityDescription
        newEntity.managedObjectClassName = "\(moduleName).\(entity.managedObjectClassName)"
        newEntities.append(newEntity)
    }

    // Set correct subentities
    for (_, entity) in enumerate(newEntities) {
        var newSubEntities = [NSEntityDescription]()
        for subEntity in entity.subentities! {
            for (_, entity) in enumerate(newEntities) {
                if subEntity.name == entity.name {
                    newSubEntities.append(entity)
                }
            }
        }
        entity.subentities = newSubEntities
    }

    // Set model
    self.managedObjectModel = NSManagedObjectModel()
    self.managedObjectModel.entities = newEntities
}
1
répondu Niel 2015-05-20 08:05:25

j'ai également fait face à un problème similaire lorsque j'ai essayé d'écrire des cas de test unitaire pour un exemple d'application ( MedicationSchedulerSwift3.0) écrit en Swift 3.0, sauf pour la mise en œuvre de la solution fournie par johnford j'ai créé une catégorie sur XCTestCase pour configurer un contexte NSManagedObject avec stockage en mémoire en utilisant le code ci-dessous:

//  XCTestCase+CoreDataHelper.swift

import CoreData
import XCTest
@testable import Medication

extension XCTestCase {
    func setUpInMemoryManagedObjectContext() -> NSManagedObjectContext {
        let managedObjectModel = NSManagedObjectModel.mergedModel(from: [Bundle.main])!

        let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)

        do {
            try persistentStoreCoordinator.addPersistentStore(ofType: NSInMemoryStoreType, configurationName: nil, at: nil, options: nil)
        } catch {
            print("Adding in-memory persistent store failed")
        }

        let managedObjectContext = NSManagedObjectContext(concurrencyType:.privateQueueConcurrencyType)
        managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator

        return managedObjectContext
    }
}

pour l'utiliser comme ceci:

//  NurseTests.swift

import XCTest
import CoreData
@testable import Medication

class NurseTests: XCTestCase {
    var managedObjectContext: NSManagedObjectContext?

    //MARK: Overriden methods
    override func setUp() {
        super.setUp()
        // Put setup code here. This method is called before the invocation of each test method in the class.
        if managedObjectContext == nil {
            managedObjectContext = setUpInMemoryManagedObjectContext()
        }
    }

//MARK:- Testing functions defined in Nurse.swift
    // testing : class func addNurse(withEmail email: String, password: String, inManagedObjectContext managedObjectContext: NSManagedObjectContext) -> NSError?
    func testAddNurse() {
        let nurseEmail = "clara@gmail.com"
        let nursePassword = "clara"

        let error = Nurse.addNurse(withEmail: nurseEmail, password: nursePassword, inManagedObjectContext: managedObjectContext!)
        XCTAssertNil(error, "There should not be any error while adding a nurse")
    }
}

dans le cas où quelqu'un a besoin de plus d'exemples, ils peuvent regarder les cas de test de l'unité plus ici - MedicationTests

1
répondu Devarshi 2017-05-23 12:18:35