2014-07-09 7 views
16

Ich arbeite an einem iOS-Projekt, das Kerndaten verwendet. Ich benutze schnell. Der Core Data Stack ist richtig eingerichtet und alles scheint in Ordnung zu sein. Ich habe eine Klasse für eine Entität (NSManagedObject) mit dem Namen TestEntity erstellt. Die Klasse sieht wie folgt aus:Swift kann Core-Daten in Xcode-Tests nicht testen?

import UIKit 
import CoreData 

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

Also, dann versuche ich diese Codezeile einen neuen TestEntity in Code einfügen mit:

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

ich dann diesen Fehler:

enter image description here

Ich habe einige Antworten auf Stapelüberlauf gesehen, die sagen, dass ich mich um den Modulnamen sorgen muss. Also schaute ich auf der Dokumentation, dass bis: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WritingSwiftClassesWithObjective-CBehavior.html

Dann ging ich in der Kerndateneinheit für TestEntity und in der Klasse Feld eingetragen I myAppName.TestEntity

Wenn ich die App diese Zeile aus:

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

gibt mir immer noch den gleichen Fehler.

Was könnte ich sonst noch falsch machen?

EDIT: So konnte ich die App nicht mehr machen Absturz durch die TestEntity NSManagedObject Klasse Wechsel: Import UIKit Import Coredata

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

Also, ich hinzugefügt, um die @objc (TestEntity) drin. Dies funktioniert mit oder ohne Hinzufügen des appName vor dem TestEntity-Klassennamen im Core Data Data Model Inspector.

Dies funktioniert, aber, wenn ich Tests diese Linie noch Abstürze laufen:

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

So fand ich, dass dies ein Problem für andere Menschen ist: How to access Core Data generated Obj-C classes in test targets?

Wie kann Wir bekommen Kerndaten in Tests in Swift zu arbeiten. Ich benutze keine Bridging-Header in der App-Ziel und alles funktioniert gut. Das Testziel stürzt trotzdem ab.

Wie kann ich das Testziel beheben, damit Kerndatentests ausgeführt werden können?

+0

Versuchen Sie Folgendes: http://StackOverflow.com/a/26568813/438063 – Lucien

+0

Weitere Anmerkungen [in dieser SO-Antwort] [1] zum Hinzufügen von @objc (ClassName) -Ansatz. [1]: http://stackoverflow.com/a/29445352/2466193 –

+0

einige weitere Hinweise siehe [in diesem SO beantworten] [1] auf der 'Zugabe von @objc (Classname)' -Ansatz. [1]: http://stackoverflow.com/a/29445352/2466193 –

Antwort

18

Mit Xcode 7 und @testable, Sie sollten nicht mehr die managedObjectClassName oder verwenden andere Hacks zu aktualisieren. Hier ist, was ich getan habe, damit es in Xcode 7.2 funktioniert.

  1. Legen Sie Ihre Test-Ziel-Host-Anwendung fest und aktivieren Sie die Option "Host-Anwendungs-APIs testen".

enter image description here

  1. Stellen Sie sicher, keinen Ihre regelmäßigen Klassen haben ein Ziel Der Mitgliedschaft in das Testziel zeigt. Nur Klassen mit Komponententestcode sollten auf das Testziel festgelegt werden.

enter image description here

  1. Fügen Sie die @testable Linie an die Spitze aller Ihrer Testklassen:
import XCTest 
@testable import MyApp 

class MyAppTests: XCTestCase { 
} 

Wenn Sie immer noch Probleme mit Sie können diese zusätzlichen Tipps versuchen: https://forums.developer.apple.com/message/28773#28949

Ich kämpfte mit diesem für eine Weile, so hoffe ich, es hilft s Omeone sonst raus.

+0

Das war re Verbündeter hilfreich ... Es hat super funktioniert +1 – Sabby

5

Ich denke, ich bekomme ähnliche Ergebnisse zu Ihnen.Ich war nicht in der Lage zu meinen Tests erhalten mit der Linie arbeiten

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

Aber ich konnte die Tests laufen mit bekommen:

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

Meine Entity wie folgt aussieht:

@objc(Department) 
class Department: NSManagedObject { 

    @NSManaged var department_description: String 
    ... 
} 
+1

Ich kann sehen, wie dies für die Schaffung neuer Einheiten funktioniert. Aber was ist, wenn Sie später Umsetzungen benötigen, zum Beispiel mit Abrufanforderungen, die Entitäten zurückgeben, die Sie in die entsprechenden Typen umwandeln müssen? –

6

Es ist, weil die Coredata Framework ist immer noch in Objective-C. Swift Anwendungen Namespace-Klassen, so dass für Coredata Ihre schnellen Klassen finden Sie den Klassennamen angeben müssen damit Namespace wie folgt ist:

enter image description here

Das Problem Ihr haben wird, ist, dass Ihre App nicht über die gleiche Namespace, wenn Sie Ihre Tests ausführen. <AppName>.<ClassName> vs <AppName>Tests.<ClassName>

EDIT: Lösung für als App läuft und Tests

Ich schrieb ein Stück Code der <AppName>.<ClassName> vs <AppName>Tests.<ClassName> Problem zu lösen. Die Lösung, die ich zu diesem Zeitpunkt verwende (Xcode 6.1), besteht darin, das Class-Feld in der CoreData-UI (siehe oben) NICHT zu füllen und stattdessen Code zu verwenden.

Dieser Code erkennt, ob Sie als App vs Tests ausgeführt werden, und verwenden Sie den richtigen Modulnamen und aktualisieren Sie die 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 
}() 
+0

Danke, das war sehr hilfreich! –

+0

Das ist die Lösung, die für mich funktioniert, und ich habe diesen Code in AppDelegate eingefügt, damit er funktioniert. Aus irgendeinem Grund kann ich es nicht zum Laufen bringen, wenn ich es direkt in meinen Unit Tests verwende. Und die Antwort von Oliver Shaw funktioniert nur für Nicht-Tests-Ziele und verwendet sie nicht zusammen mit dieser Lösung, da ich festgestellt habe, dass diese Lösung nicht funktioniert. – nybon

+0

Ich liebe die Idee und sehe, wie es funktionieren könnte .... solange Sie kein NSPersistentDocument auf MacOS verwenden. Problem scheint zu sein, dass NSPersistentDocument mit einem Kontext kommt, der bereits mit einem Modell fertig ist. Die Initialisierung eines NSManagedObject-Abkömmlings schlägt dann fehl, da das interne Modell die Entität nicht kennt. :-( –

1

Das Codebeispiel von Ludovic deckt Untergruppen nicht ab. Wenn also eine übergeordnete Entität in CoreData festgelegt wird, stürzt die App ab.

den Code Angepasst Unter-Entitäten zu berücksichtigen:

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

ich auch ähnliches Problem konfrontiert, wenn ich versuchte, von der Einheit Testfälle für eine Beispielanwendung (MedicationSchedulerSwift3.0) geschrieben in Swift 3.0, auseinander zu schreiben solution provided by johnford Umsetzung habe ich eine Kategorie auf XCTestCase Setup ein NSManagedObjectContext mit In-Memory-Speicher unter Verwendung von Code unten:

// 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 
    } 
} 

und benutzte es wie folgt aus:

// 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 = "[email protected]" 
     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") 
    } 
} 

Im Fall, wenn jemand mehr Beispiele braucht sie bei Einheit Testfälle hier sehen kann - MedicationTests

Verwandte Themen