ios - xcode para windows
¿Swift no puede probar datos básicos en pruebas de Xcode? (5)
Con Xcode 7 y @testable
, ya no debería necesitar actualizar el managedObjectClassName
o usar otros hacks. Esto es lo que hice para que funcione en Xcode 7.2.
- Configure su aplicación de host objetivo de prueba y marque "Permitir la prueba de las API de aplicaciones de host".
- Asegúrese de que ninguna de sus clases regulares tenga una membresía de destino que apunte al objetivo de prueba. Sólo las clases con código de prueba de unidad deben establecerse en el objetivo de prueba.
- Agregue la línea
@testable
en la parte superior de todas sus clases de prueba:
import XCTest
@testable import MyApp
class MyAppTests: XCTestCase {
}
Si aún tiene problemas, puede intentar estos consejos adicionales: https://forums.developer.apple.com/message/28773#28949
Luché con este por un tiempo, así que espero que ayude a alguien más.
Estoy trabajando en un proyecto de iOS que utiliza datos básicos. Estoy usando swift. La pila de Core Data está bien configurada y todo parece estar bien. He creado una clase para una entidad (NSManagedObject) llamada TestEntity. La clase se ve así:
import UIKit
import CoreData
class TestEntity: NSManagedObject {
@NSManaged var name: NSString
@NSManaged var age: NSNumber
}
Entonces, trato de insertar una nueva TestEntity en el código usando esta línea de código:
let te: TestEntity = NSEntityDescription.insertNewObjectForEntityForName("TestEntity", inManagedObjectContext: ctx) as TestEntity
Entonces me sale este error:
He visto algunas respuestas en el desbordamiento de pila que dicen que debo preocuparme por el nombre del módulo. Entonces busqué en los documentos: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WritingSwiftClassesWithObjective-CBehavior.html
Luego entré en la entidad de datos central para TestEntity y en el campo de clase ingresé myAppName.TestEntity
Cuando corro la aplicación esta línea:
let te: TestEntity = NSEntityDescription.insertNewObjectForEntityForName("TestEntity", inManagedObjectContext: ctx) as TestEntity
Todavía me da el mismo error.
¿Qué otra cosa podría estar haciendo mal?
EDIT: Entonces, pude hacer que la aplicación no se bloquee más cambiando la clase TestEntity NSManagedObject para: importar UIKit importar CoreData
@objc(TestEntity) class TestEntity: NSManagedObject {
@NSManaged var name: NSString
@NSManaged var age: NSNumber
}
Entonces, agregué el @objc (TestEntity) en él. Esto funciona con o sin agregar el nombre de la aplicación antes del nombre de la clase TestEntity en el inspector del modelo de datos de datos centrales.
Esto funciona, pero, cuando ejecuto pruebas, esta línea aún falla :
let te: TestEntity = NSEntityDescription.insertNewObjectForEntityForName("TestEntity", inManagedObjectContext: ctx) as TestEntity
Entonces, descubrí que este es un problema para otras personas: ¿Cómo acceder a las clases de Obj-C generadas por Core Data en los objetivos de prueba?
¿Cómo podemos obtener datos básicos para trabajar en las pruebas de forma rápida . NO estoy usando un encabezado puente en el destino de la aplicación y todo funciona muy bien. Sin embargo, el objetivo de prueba todavía se bloquea.
¿Cómo puedo corregir el objetivo de la prueba para que pueda ejecutar pruebas de datos centrales?
Creo que estoy obteniendo resultados similares a ustedes. No pude hacer mis pruebas trabajando con la línea
var newDept = NSEntityDescription.insertNewObjectForEntityForName("Department", inManagedObjectContext: moc) as Department
Pero podría hacer que las pruebas se ejecuten con:
let entity = NSEntityDescription.entityForName("Department", inManagedObjectContext: moc)
let department = Department(entity: entity!, insertIntoManagedObjectContext: moc)
Mi entidad se parece a:
@objc(Department)
class Department: NSManagedObject {
@NSManaged var department_description: String
...
}
El ejemplo de código de Ludovic no cubre subentidades. Entonces, cuando se configura una entidad principal en CoreData, la aplicación se bloquea.
Adaptó el código para tener en cuenta las subentidades:
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
}
Es porque el marco CoreData todavía está en Objective-C. Swift utiliza las clases de espacio de nombre, por lo que para que CoreData encuentre sus clases de Swift, debe especificar el nombre de la Clase con su espacio de nombre como este:
El problema que tendrá es que su aplicación no tiene el mismo espacio de nombres que cuando ejecuta las pruebas. <AppName>.<ClassName>
vs <AppName>Tests.<ClassName>
EDITAR: Solución para ejecutar como aplicación y pruebas
Acabo de escribir un fragmento de código para resolver el problema <AppName>.<ClassName>
vs <AppName>Tests.<ClassName>
. La solución que uso en este momento (Xcode 6.1) es NO rellenar el campo Class
en la interfaz de usuario CoreData (que se muestra arriba), y hacerlo en el código.
Este código detectará si se está ejecutando como App vs Tests, usar el nombre correcto del módulo y actualizar el 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
}()
También tuve problemas similares cuando intenté escribir casos de prueba de unidad para una aplicación de muestra ( MedicationSchedulerSwift3.0 ) escrita en Swift 3.0, además de implementar la solución provista por johnford . utilizando el siguiente código:
// 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
}
}
Y lo usé así:
// 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")
}
}
En caso de que alguien necesite más ejemplos, puede ver los casos de prueba unitaria aquí - MedicationTests