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:
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.
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 ).
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.
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:
So to add to jessesquires answer above, append @testable to your import, and this should solve the unit test errors:
@testable import MyAppTarget
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With