1

I have a managed object called Gift which has the properties

import Foundation
import CoreData

class Gift: NSManagedObject {

    @NSManaged var name: String
    @NSManaged var price: NSNumber
    @NSManaged var location: String

}

Using a single unit test I then insert an entity into Core Data successfully (no error).

func testThatWeCanSaveGift() {
        let entity = NSEntityDescription.entityForName("Gift", inManagedObjectContext: managedObjectContext!)
        let gift = Gift(entity: entity!, insertIntoManagedObjectContext: managedObjectContext!)
        gift.name = "Test"
        gift.price = 10.0
        gift.location = "London"

        XCTAssertNotNil(gift, "Unable to create a gift")

        var error: NSError? = nil
        managedObjectContext?.save(&error)

        XCTAssertNil(error, "Failed to save the context with error \(error), \(error?.userInfo)")

To test that this was completely successful I then use a fetch request to return this data back.

        let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: "Gift")
        var requestError: NSError? = nil
        if let gifts = managedObjectContext?.executeFetchRequest(fetchRequest, error: &requestError) {
            let aGift: NSManagedObject = gifts.first as! NSManagedObject
            //println("Gift name: \(aGift.name)")
            let string: String? = aGift.valueForKey("name") as? String
            println("Name: \(string)")
            var bGift: Gift = aGift as! Gift
            println("Name: \(bGift.name)")
            var a = 1
        }
    }

When I run this test it fails on the line that casts aGift to bGift var bGift: Gift = aGift as! Gift with the error EXC_BAD_ACCESS(code=1, address=0x8). However, I do get the results from the Core Data in the aGift but as a NSManagedObject

This only happens when I run the the code in a unit test, if I run it in the application it returns the correct information and casts it correctly.

What am I doing wrong for testing?

Carl Thomas
  • 3,605
  • 6
  • 38
  • 50

1 Answers1

1

Found the answer on this page (the first answer after question) Swift and CoreData Casting issues in test vs non-test

The reason I could not cast was that the NSManagedObject(Gift) fully qualified class name was MY_PROJECT.Gift, but when the test runs it tries to cast it to MY_PROJECT_TESTS.Gift. To combat this I removed the MY_PROJECT from the class name for the entity in the Core Data Model UI, so it just read Gift. Then I re-wrote the managedObjectModel code to.

lazy var managedObjectModel: NSManagedObjectModel = {
        // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
        let modelURL = NSBundle.mainBundle().URLForResource("GiftReminder", 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) ? "GiftReminderTests" : "GiftReminder"

        // 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
    }()

This will dynamically check to see if I am running a test or not and use the correct pre-fixer to the class.

Community
  • 1
  • 1
Carl Thomas
  • 3,605
  • 6
  • 38
  • 50