CoreData class miss match in unit test

When i try to retrieve an array of items from my model I always get an down cast error, because of a mismatch of the types of the classes. Swift has a strict namespace and the model items are different than the items I want to cat into. Here is my NSManagedObject:

import Foundation
import CoreData

@objc(Boss)
class Boss: NSManagedObject {    
    @NSManaged var name: String
}

The Testclass is following:

  • How to organize unit testing of a library project in Xcode?
  • “A valid provisioning profile for this executable was not found” error when trying to run unit tests
  • How to run a fresh install of the application every time unit tests are run?
  • UIApplication.sharedApplication().delegate as AppDelegate causes EXC_BAD_ACCESS using it on swift unit test
  • Unit testing thread-based code? Forcing a race condition
  • Can OCMock run a block parameter?
  • func testCheckIfFetchGetTheCorrectClass() {
       // setup item
       let entity = NSEntityDescription.entityForName("Boss", inManagedObjectContext: moc)
       let boss = Boss(entity: entity!, insertIntoManagedObjectContext: moc)
    
      boss.name = "Chef"
    
      var bosses = [Boss]()
    
      var request = NSFetchRequest(entityName: "Boss")
      var e: NSError?
      if let results = moc.executeFetchRequest(request, error: &e) {
        println("results: \n\(results.description)\nCount:\(results.count)")
        if let downcastedSwiftArray = results as? [Boss] {
          // downcastedSwiftArray contains only UIView objects
          bosses = downcastedSwiftArray
        } else  {
          XCTAssert(false, "Down Cast Error")
        }
        println("Bosses : \n\(bosses.description)")
      } else {
        println("fetch error: \(e!.localizedDescription)")
        abort();
      }
    
        // This is an example of a functional test case.
        XCTAssert(true, "Pass")
    }
    

    When I run the test following types will show up in the debugger:

    bosses  [NameSpaceTestTests.Boss]   0 values        
    results [AnyObject] 1 value 
      [0]   Boss_Boss_ *    
    

    So it looks like the result array contains a Boss class item that will not match the bosses
    array.

    How can I assign the result of the fetch request to my array?

    You will find the complete project on github.

    4 Solutions Collect From Internet About “CoreData class miss match in unit test”

    Making your classes public is a workaround and should not be needed for test dependencies.

    The problem really is that the test target compiles his own version of the source classes, which isn’t what you want. You’ll end up with two versions of the same.

    The following steps fixed it for me:

    Remove all ‘normal’ classes from the test target compile list

    enter image description here


    Make sure the Module name for the app and test app are different (i.e. App and AppTests

    enter image description here
    enter image description here


    Make sure you import the app module in every test

    enter image description here

    If you change

    if let downcastedSwiftArray = results as? [Boss] {
    

    to force downcast to

    if let downcastedSwiftArray = results as! [Boss] {
    

    does your test abort due to a runtime error?

    I had problems downcasting to Core Data managed object types as well. Adding the NSManagedObject sublcasses’ files to the test target made the code compile but didn’t seem to work properly. So don’t add code under test to the test target itself. Import it.

    With Swift 2: Use @testable

    Before Swift introduced the @testable import, we were forced to make classes we wanted to test public. That exposed far more of our code to client than we’d want. Nowadays, this is the way to go.

    Before Swift 2: Make the class public

    I solved this by making the classes public:

    @objc(Boss)
    public class Boss: NSManagedObject {    
        @NSManaged public var name: String
    }
    

    Then import the production module in tests:

    import MyProjectTargetName
    

    In Swift you have to specify the Type of the array resulting from a fetch request. The if let pattern requires an optional cast.

    if let result = moc.executeFetchRequest(request, error:&e) as? [Boss] {
        // use result
    }
    

    One way to solve this is to dynamically alter the class name of the NSManagedObject subclass with something like:

        let managedObjectModel = NSManagedObjectModel.mergedModelFromBundles([NSBundle.mainBundle()])!
    
        // Check if it is within the test environment
        let environment = NSProcessInfo.processInfo().environment as! [String : AnyObject]
        let isTestEnvironment = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"
    
        // Create the module name based on product name 
        let productName:String = NSBundle.mainBundle().infoDictionary?["CFBundleName"] as! String
        let moduleName = (isTestEnvironment) ? productName + "Tests" : productName
    
        let newManagedObjectModel:NSManagedObjectModel = managedObjectModel.copy() as! NSManagedObjectModel
    
        for entity in newManagedObjectModel.entities as! [NSEntityDescription] {
            entity.managedObjectClassName = "\(moduleName).\(entity.name!)"
        }
    

    This helps to workaround the class name mismatch because when running the test, the class name is actually <Product Name>Tests.<Subclass Name> (in your case, it’s NameSpaceTestTests.Boss)