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:
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?
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.
- Définissez votre application hôte cible de test et cochez "autoriser la mise à l'essai des applications hôtes API".
- 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.
- 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.
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:
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
}()
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
...
}
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
}
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