Why doesn't executeFetchRequest return subclass objects when run under XCTest using Swift?

I have a Swift project using CoreData with installation subclasses of NSManagedObject. They have a class name setting (in the xcdatamodel view), which includes as the main target name "MainTarget.MyEntity" according to. Everything works well when the application starts. However, I went to add some Unit tests using XCTest, and the application crashed with ERR_BAD_ACCESS or ERR_BAD_INSTRUCTION when it returned the result of executeFetchRequest to the appropriate type. I also tried casting every item in the array, and this also causes a crash. The error in all my tests is zero.

var entities = _managedContext.executeFetchRequest(fetchRequest, error: &error) as [MyEntity]?

Above results: "Fatal error: NSArray element could not match Swift Array element type" on the console.

var entities = _managedContext.executeFetchRequest(fetchRequest, error: &error)
let e = entities[0] // Works fine
let ee = e as NSManagedObject // Works fine
let eee = e as MyEntity // Crash: Exception breakpoint, then ERR_BAD_ACCESS - no messages on console

The above causes a crash, but only when called from unit test!

I have two goals, one main goal and one test goal (normal default setting). NSManagedObject classes are selected for both purposes so that they are available for testing. These subclasses are written in Swift, no Objective-C.

During unit tests, I use persistentStoreCoordinator in memory (although I tried with the same setting in AppDelegate with the same result).

Swift (, ). , executeFetchRequest, , , "po" XCode - SourceKit 3 , ... . , , , TestTarget.MyEntity MainTarget.MyEntity. , MainTarget.MyEnity( MainTarget) , , .

, , unit test , , , . - XCTests NSManagedObject? CoreData MainTarget, , TestTarget?

, , :

+4
3
+1

: - . .

, @madcat, :

  • public

    public class MyManagedObject: NSManagedObject {
        @NSManaged public var myProperty: String
    
  • do

  • ( : ) XCTest

    import TargetName
    
  • TargetName.MyManagedObject, @objc() :

    @objc(MyManagedObject)
    public class MyManagedObject: NSManagedObject {
    

Swift 2: Swift 2 . : @testable import TargetName.

[...] a unit test target , @testable .

from : - unit test

+1

As you noticed from your debugger, the problem is that when you run the test, the subclass NSManagedObjectis actually <Product Name>Tests.<Subclass Name>instead <Product Name>.<Subclass Name>. One way to solve this is to dynamically change the class name of the subclass NSManagedObjectwith 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 circumvent class name mismatch.

+1
source

All Articles