samedi 1 août 2015

Unit Testing Core Data in XCode 7 Beta/Swift 2

In a Nutshell:

I'm trying to write a unit test for a Core Data model in XCode 7/Swift 2. However, a simple test such as testing that the number of rows in the model is equal to what I know it to be fails (I know there to be one row, but the test sees none). My best guess is that I'm getting a different (clean?) version of managedObjectContext, but I'm not sure how to get the same version as the app. Besides the technique described below (following the linked instructions on Andrew Bancroft's site), I tried adding the application delegate to SOTestTests and getting the managedObjectContext as follows...

let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

...but I then get an error that XCode [c]ould not cast value of type 'SOTest.AppDelegate' (0x25bb8) to 'SOTestTests.AppDelegate' (0x2c7bb78).

Longform:

Here are the steps to set up the smallest reproducible example I could come up with:

  1. Simple model: One Entity (Person) with one Attribute (name, type String). Person is unique, name is non-optional.
  2. Create a subclass (Editor > Create NSManagedObject Subclass...)
  3. (Comment out @objc(Person) in Person.swift to avoid a build error.)
  4. Add a single row to the model. (Only run the code that gets an entity and saves it once--comment it out on subsequent runs to avoid getting a conflict error due to the unique property.) Here's the ViewController that's doing this:
import UIKit
import CoreData

class ViewController: UIViewController {

    let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

    override func viewDidLoad() {
        super.viewDidLoad()

        // Put a new person in the model.
        let newPerson = NSEntityDescription.insertNewObjectForEntityForName("Person", inManagedObjectContext: self.managedObjectContext) as! Person
        newPerson.name = "Harper Lee"

        // Save it
        do { try self.managedObjectContext.save()
        } catch _ { NSLog("There was a problem saving to the database.") }

        // Sanity check
        let fetchRequest = NSFetchRequest(entityName:"Person")

        do {
            let fetchedResults = try managedObjectContext.executeFetchRequest(fetchRequest) as? [Person]
            NSLog("Number of rows (App): \(fetchedResults!.count)") // 1
        } catch _ { NSLog("Well...that went badly.") }
    }
}

  1. I then add the Person subclass files to SOTestTests (meta, I know) in Build Phases, setup the managedObjectContext according to the example on Andrew Bancroft's (excellent) site (with mods for Swift 2), and write the test checking if there is one row:
import XCTest
import CoreData

class SOTestTests: XCTestCase {

    var managedObjectContext: NSManagedObjectContext!

    func setUpInMemoryManagedObjectContext() -> NSManagedObjectContext {
        let managedObjectModel = NSManagedObjectModel.mergedModelFromBundles([NSBundle.mainBundle()])!

        let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)

        do {
            try persistentStoreCoordinator.addPersistentStoreWithType(NSInMemoryStoreType, configuration: nil, URL: nil, options: nil)
        } catch _ { NSLog("Problem adding the Persistent Store Coordinator") }

        let managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
        managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator

        return managedObjectContext
    }

    override func setUp() {
        super.setUp()

        self.managedObjectContext = self.setUpInMemoryManagedObjectContext()

    }

    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }

    func testExample() {
        // Check the number of rows in NUT_DATA
        do {
            let fetchRequest = NSFetchRequest(entityName:"Person")
            let fetchedResults = try managedObjectContext.executeFetchRequest(fetchRequest) as? [Person]
            print("Number of rows (Test): \(fetchedResults!.count)") // Prints 0
            XCTAssert(1 == fetchedResults!.count, "Incorrect number of rows in `Person` entity.")
        } catch _ { NSLog("Error fetching the entity Person.") }
    }
}

  1. Create a new scheme by selecting SOTestTests, and addthe SOTest.app executable to the run scheme.

When I run this, the ViewController class prints out that there is a single row, but the test prints out zero rows, and the test fails.

Any ideas what I'm doing wrong?

Aucun commentaire:

Enregistrer un commentaire