2014-09-12 9 views
7

Ho un app Swift che utilizza NSFetchedResultsController per recuperare List oggetti da archivio permanente:Come unità-test NSFetchedResultsController a Swift

let fetchedResultsController: NSFetchedResultsController = ... 
var error : NSError? 
fetchedResultsController.performFetch(&error) 
if let error = error { 
    NSLog("Error: \(error)") 
} 
let lists: [List] = fetchedResultsController.fetchedObjects! as [List] 
NSLog("lists count = \(lists.count)") 
for list: List in lists { 
    NSLog("List: \(list.description)") 
} 

e funziona come previsto, io sono sempre List oggetti descrizioni stampate al console. Vorrei scrivere alcuni test unitari per la mia app, quindi ho creato la classe che estende XCTestCase. Il codice viene compilato senza problemi, i test vengono eseguiti, ma sfortunatamente non sono in grado di recuperare gli oggetti in quel contesto.

tutto io sono sempre nella console è conteggio di List oggetti e un errore fatale:

lists count = 59 
fatal error: NSArray element failed to match the Swift Array Element type 

rizzati dalla linea:

for list: List in lists { 

Sono abbastanza sicuro di avere obiettivi configurati correttamente, come posso creare l'oggetto List e inserirlo nel contesto dell'oggetto gestito senza problemi dal codice sorgente della mia app e dal codice sorgente del test dell'unità. L'unico problema che sto vivendo è con il recupero dall'unità di test. Mi chiedo perché il recupero funzioni quando si esegue l'app nel simulatore e fallisce quando viene eseguito durante il test dell'unità.

Tutte le idee su ciò che potrebbe essere sbagliato saranno apprezzate.

Aggiornamento:

Per essere più specifici come la mia implementazione sembra, qui è esempio di codice completo che sto giocando con:

var error: NSError? = nil 

let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) 
let applicationDocumentsDirectory = urls[urls.count-1] as NSURL 

let modelURL = NSBundle.mainBundle().URLForResource("CheckLists", withExtension: "momd")! 
let managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL) 

var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel) 
let url = applicationDocumentsDirectory.URLByAppendingPathComponent("CheckLists.sqlite") 
if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil { 
    NSLog("Error1: \(error)") 
    abort() 
} 

var managedObjectContext = NSManagedObjectContext() 
managedObjectContext.persistentStoreCoordinator = coordinator 

let fetchRequest = NSFetchRequest() 
fetchRequest.entity = NSEntityDescription.entityForName("List", inManagedObjectContext: managedObjectContext) 
fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "name", ascending: true) ] 

let fetchedResultsController = NSFetchedResultsController(
    fetchRequest: fetchRequest, 
    managedObjectContext: managedObjectContext, 
    sectionNameKeyPath: nil, 
    cacheName: "ListFetchedResultsControllerCache" 
) 

fetchedResultsController.performFetch(&error) 
if let error = error { 
    NSLog("Error2: \(error)") 
    abort() 
} 

let fetchedObjects: [AnyObject]? = fetchedResultsController.fetchedObjects 
if let fetchedObjects = fetchedObjects { 
    NSLog("Fetched objects count: \(fetchedObjects.count)") 
    for fetchedObject in fetchedObjects { 
     NSLog("Fetched object: \(fetchedObject.description)") 
    } 
} 
else { 
    NSLog("Fetched objects array is nil") 
} 

let fetchedLists: [List]? = fetchedResultsController.fetchedObjects as? [List] 
if let fetchedLists = fetchedLists { 
    NSLog("Fetched lists count: \(fetchedLists.count)") 
    for fetchedList in fetchedLists { 
     NSLog("Fetched list: \(fetchedList.description)") 
    } 
} 
else { 
    NSLog("Fetched lists array is nil") 
} 

Quando eseguo dal codice sorgente del mio app, eseguendo l'app nel simulatore, l'output della console è simile al seguente:

Fetched objects count: 3 
Fetched object: <CheckLists.List: 0x7a6866f0> (entity: List; id: 0x7a686020 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p2> ; data: { 
    name = "List 1"; 
}) 
Fetched object: <CheckLists.List: 0x7a686930> (entity: List; id: 0x7a686030 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p1> ; data: { 
    name = "List 2"; 
}) 
Fetched object: <CheckLists.List: 0x7a686970> (entity: List; id: 0x7a686040 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p3> ; data: { 
    name = "List 3"; 
}) 
Fetched lists count: 3 
Fetched list: <CheckLists.List: 0x7a6866f0> (entity: List; id: 0x7a686020 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p2> ; data: { 
    name = "List 1"; 
}) 
Fetched list: <CheckLists.List: 0x7a686930> (entity: List; id: 0x7a686030 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p1> ; data: { 
    name = "List 2"; 
}) 
Fetched list: <CheckLists.List: 0x7a686970> (entity: List; id: 0x7a686040 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p3> ; data: { 
    name = "List 3"; 
}) 

Tuttavia, quando eseguo questo codice da un unit test, sto ottenendo questo risultato:

Fetched objects count: 3 
Fetched object: <CheckLists.List: 0x7a07df50> (entity: List; id: 0x7a07d7e0 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p2> ; data: { 
    name = "List 1"; 
}) 
Fetched object: <CheckLists.List: 0x7a07e190> (entity: List; id: 0x7a07d7f0 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p1> ; data: { 
    name = "List 2"; 
}) 
Fetched object: <CheckLists.List: 0x7a07e1d0> (entity: List; id: 0x7a07d800 <x-coredata://7A87B5BE-C2FA-4150-B9E3-879FDE07F0B9/List/p3> ; data: { 
    name = "List 3"; 
}) 
Fetched lists array is nil 

Spero che sia più facile capire dove si trova il problema. In qualche modo, questa affermazione:

let fetchedLists: [List]? = fetchedResultsController.fetchedObjects as? [List] 

produce una matrice di List oggetti quando app viene eseguito nel simulatore, ma fallisce producendo nil quando eseguito dal test di unità.

+0

Il file del modello di oggetto ("mamma") è incluso nel pacchetto di test? Ed è la configurazione del file del modello per utilizzare la sottoclasse 'Elenco' per l'entità dell'elenco? – MrAlek

+0

@MrAlek - Non sono sicuro che il mio file '.momd' sia incluso nel bundle di test. Sto creando un modello del genere: 'NSManagedObjectModel (NSBundle.mainBundle(). URLForResource (" CheckLists ", withExtension:" momd ")!)' E funziona sia per il target principale che per il target di test. Potresti essere più specifico? – Darrarski

+0

@MrAlek - Nel mio file '.xcdatamodeld' ho impostato la classe corretta per l'entità' List'. Posso persino creare una nuova entità nei miei test unitari e inserirla nel contesto. Rimane nel database quando il contesto viene salvato. – Darrarski

risposta

2

Il problema è connesso alla configurazione dei target. Ho risolto il problema con un po 'di soluzione.

In precedenza, per rendere la classe di entità List accessibile nel mio target di test dell'unità, l'ho aggiunta a questa destinazione. Quindi, la classe era in due obiettivi. Infatti, c'erano due classi List note da Swift, una per ciascun target: MyAppTarget.List e MyUnitTestTarget.List. Il controller dei risultati ritardato restituisce un array di oggetti MyAppTarget.List, ma nel target di test unitario, List è stato assunto come classe MyUnitTestTarget.List.Questo è così questa riga di codice:

let fetchedLists: [List]? = fetchedResultsController.fetchedObjects as? [List] 

prodotte nil quando eseguiti dal bersaglio test di unità, e non la matrice appropriata come quando eseguito dalla porta principale. Per risolvere che ho appena cambiato in:

let fetchedLists: [MyAppTarget.List]? = fetchedResultsController.fetchedObjects as? [MyAppTarget.List] 

e rendere il pubblico List di classe. Dopo questo cambiamento, funziona come previsto.

Tuttavia, è un po 'di confusione per me che MyAppTarget.List non possa essere convertito in MyUnitTestTarget.List. Inoltre, significa che ho bisogno di rendere pubblica ogni sottoclasse di entità NSManagedObject per poterlo utilizzare all'interno dei test unitari. Finora non ho trovato una soluzione migliore.

Forse c'è un modo migliore per risolvere questo problema. Non vedo un'opzione per dire a NSFetchedResultsController che deve restituire MyAppTarget.List nell'obiettivo principale e MyUnitTestTarget.List nell'unità di destinazione del test. Utilizzerà sempre la configurazione dal file per una determinata entità. Inoltre, anche se esiste un modo per trasmettere MyAppTarget.List in MyUnitTestTarget.List all'interno di un test di unità, sarà comunque necessario che la classe sia pubblica.

Aggiornamento:

ho trovato un modo per cambiare classe di entità restituiti da NSFetchedResultsController in fase di esecuzione. È una soluzione più chiara e semplice: https://stackoverflow.com/a/25858758/514181

Consente di utilizzare senza problemi le entità CoreData nei test di unità, senza eseguire il cast o rendere pubblica la classe di entità.

+0

Hai un esempio da qualche parte? Ho provato questo approccio, ma ho riportato errori dicendo che la classe non è stata trovata quindi stava usando NSManagedObject invece –

+1

Ho allegato il codice sorgente di esempio qui: http://stackoverflow.com/a/25858758/514181 – Darrarski

+1

Ho provato quello ma no gioia. Ho un semplice esempio su https://github.com/ztolley/Frazzle –

Problemi correlati