Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test Core Data properly in Swift

There are quite a few subjects on this already, but I have yet to find a solution that is workable for Swift (Xcode 6.2).

To test Core Data backed classes in Swift, I generate new Managed Object Contexts that I then inject into my classes.

//Given   
let testManagedObjectContext = CoreDataTestComposer.setUpInMemoryManagedObjectContext()
let testItems = createFixtureData(testManagedObjectContext) as [TestItem]
self.itemDateCoordinator.managedObjectContext = testManagedObjectContext

//When
let data = self.itemDateCoordinator.do()

//Then
XCTAssert(data.exists)

The issue comes from passing a MOC created in the Test to the class that's doing. Because entity classes are namespaced, Core Data won't fetch your the appropriate ManagedObject subclass and instead hands back a NSManagedObject set. When looping or doing anything with these objects (which in your class would be an array of test items ([TestItem]).

For example, the offending class ItemDateCoordinator would execute this loop (after pulling the relevant data from a NSFetchRequest)"

for testItem in testItems {
    testItem.doPart(numberOfDays: 10)
}

would result in:

fatal error: NSArray element failed to match the Swift Array Element type

Also, I have come across a collection of information without much of a solid answer:

  • To cast entities when creating them, I have been using a solution by Jesse, but that doesn't work on a larger scope of testing.
  • A solution has been posted on another question that involved swapping out the classes at runtime, but that hasn't worked for me with entity inheritance.
  • Is there another method to testing your objects with Core Data in this case? How do you do it?
like image 608
Dandy Avatar asked Apr 14 '15 00:04

Dandy


People also ask

How do you write unit tests in Swift?

Click on the Test Navigator tab in the Xcode Navigator (the left-most window). This tab can also be reached by pressing ⌘ + 6. In the bottom left corner you can add the test target by pressing the '+' button. Follow the instructions on the screen and voila: You are now ready to write your first unit test.

What is Persistentcontainer in Core Data?

NSPersistentContainer simplifies the creation and management of the Core Data stack by handling the creation of the managed object model ( NSManagedObjectModel ), persistent store coordinator ( NSPersistentStoreCoordinator ), and the managed object context ( NSManagedObjectContext ).


2 Answers

I was about to point you toward Swift, Core Data, and unit testing but see you've already found it. :)

That post doesn't elaborate much on where your files should exist (i.e., in which Target). You should not add NSManagedObject subclasses (or any files really) to both targets. I've found that this leads to all kinds hard discover bugs and cryptic errors.

And definitely DO NOT do this. That is a terrible hack.

Instead, make your classes public and import MyAppTarget in your XCTestCase files. Better yet, your model should be in its own framework as I mention in my recent talk (a video will be posted in a few weeks on realm.io). Doing this makes your models namespace very clear and generally easier to deal with. Then you'll need to import MyAppModel everywhere you access your managed objects.

I also have a new framework, JSQCoreDataKit that intends to make Core Data easier to use in Swift. One key part of this framework is the CoreDataStack which you can initialize using an in-memory store for your tests. There's demo app with examples, and well-commented unit tests.

like image 30
jessesquires Avatar answered Sep 21 '22 16:09

jessesquires


I believe this was updated recently (iOS 9/Swift 2.0) to have the testable keyword on an imported target, mean that the target's internal classes (the default) become public. From the docs:

enter image description here

So to add to jessesquires answer above, append @testable to your import, and this should solve the unit test errors:

@testable import MyAppTarget
like image 141
PostCodeism Avatar answered Sep 22 '22 16:09

PostCodeism