2014-10-28 26 views
4

我有一個使用CoreData與NSManagedObject安裝程序的子類的Swift項目。這些類有一個類名稱設置(在xcdatamodel視圖中),它包含作爲類的主目標名稱「MainTarget.MyEntity」。運行應用程序時一切正常。但是,我在XCTest中添加了一些單元測試,並且在將executeFetchRequest的結果轉換爲適當類型時,應用程序開始與ERR_BAD_ACCESS或ERR_BAD_INSTRUCTION一起崩潰。我也嘗試鑄造數組中的每個元素,這也會導致崩潰。在我的所有測試用例中,錯誤都是零。爲什麼在使用Swift在XCTest下運行時,executeFetchRequest不會返回子類對象?

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

以上結果顯示:控制檯上的「致命錯誤:NSArray元素無法與Swift數組元素類型匹配」。

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 

上述結果導致崩潰,但僅在從單元測試中調用!

我有兩個目標,一個主要目標和一個測試目標(正常默認設置)。 NSManagedObject類被選中在兩個目標中,以便它們可以被測試訪問。這些子類是用Swift編寫的,沒有Objective-C。

在單元測試期間,我在內存中使用persistentStoreCoordinator(儘管我在AppDelegate中嘗試了相同的結果)。

Swift的調試器幾乎沒用(很抱歉不得不發泄)。當我查看由executeFetchRequest返回的數組的變量時,它不會展開以顯示元素,任何試圖「po」它或其內容的嘗試都會使XCode完全崩潰 - 與每3分鐘一次的SourceKit崩潰相結合,令人沮喪的一天...但我離題了。我可以使用的唯一有用的信息是返回的數組是TestTarget.MyEntity而不是MainTarget.MyEntity。我認爲,在投射時,它試圖投射到MainTarget.MyEnity(因爲測試中的代碼駐留在MainTarget中),並因此嘗試崩潰。

它似乎是這樣一個基本的事情,能夠單元測試涉及核心數據的代碼,我不敢相信這是多麼令人沮喪。有沒有人能夠讓XCTests與NSManagedObject子類一起工作?如何讓CoreData返回MainTarget中的正確子類而不是TestTarget中的子類?

一些其他的事情我試圖包括:

  1. 添加@objc(myEntity所)註釋子類,但是關於類未找到此引起從核心數據的消息,並因此返回NSManagedObject實例(其顯然不能強制轉換爲子類)
  2. 鏈接到這裏提到的建議:How to unit-test NSFetchedResultsController in Swift
  3. 建議位置:executeFetchRequest throw fatal error: NSArray element failed to match the Swift Array Element type
  4. 敲打靠牆頭,直到頭暈目眩。我建議不要這樣做,這不是很有效率。

回答

1

我有一個類似的問題。讓我的實體班公開解決這個問題,我得到正確的班級類型。(CoreData class miss match in unit test

+0

是的,這是我能解決它的唯一方法。我不喜歡所有我的實體和他們的變數都是公開的,感覺不對,但沒有其他的我嘗試過。 – N8P 2014-12-01 14:42:31

1

我同意:問題是純粹的地獄。調試器沒有幫助。

在@ madcat的回答說明,其中一個重要的另外的解決方法是:

  1. 有模型類及其屬性公共

    public class MyManagedObject: NSManagedObject { 
        @NSManaged public var myProperty: String 
    
  2. 在測試目標上有型號

  3. 導入項目的生產目標(默認:項目名稱)在XCTest

    import TargetName 
    
  4. 要麼TargetName.MyManagedObject在模型中定義的類或使用模型類@objc()註釋:

    @objc(MyManagedObject) 
    public class MyManagedObject: NSManagedObject { 
    

swift 2:從Swift 2開始,模型屬性做不是必須是公共了。爲此導入項目:@testable import TargetName

[…] a unit test target can access any internal entity, if you mark the import declaration for a product module with the @testable attribute and compile that product module with testing enabled.

The Swift Programming Language: Access Control – Access Levels for Unit Test Targets

+0

我無法從測試目標中刪除管理對象,因爲其他也在測試目標上的類使用該對象。由於這些類是常規目標類,因此我無法在其中導入TargetName。 – 2015-03-12 06:54:37

+0

您是否找到其他解決方案?我也不想讓這些課程導入並公開。是不是可以將目標導入自己? – dreamlab 2015-03-12 19:54:53

1

正如您從調試發現,問題出在運行測試時,該NSManagedObject的子類,實際上是<Product Name>Tests.<Subclass Name>而不是<Product Name>.<Subclass Name>。解決方法之一是動態改變NSManagedObject子類的類名的東西,如:

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!)" 
    } 

這有助於要解決的類名稱不匹配。

相關問題