测试与非测试中的 Swift 和 CoreData Casting 问题

Posted

技术标签:

【中文标题】测试与非测试中的 Swift 和 CoreData Casting 问题【英文标题】:Swift and CoreData Casting issues in test vs non-test 【发布时间】:2014-07-08 16:21:18 【问题描述】:

我在 Xcode 6 上使用带有 CoreData 的 Swift。

我已经阅读了发行说明并看到了这个issue,关于确保使用模块名称(应用名称)标记核心数据模型,以便您可以在运行时将NSManagedObject 转换为您的模型类型.

当我这样做时,我可以让应用程序正常运行(很好!)。但是,我的问题是,当我尝试 test 相同的代码时,只要演员发生Swift dynamic cast failed 错误(坏:(),测试总是会崩溃。这使得测试我的应用。

在构建应用程序以进行测试与运行时,我们使用的模块名称是否有任何影响?

提前感谢您的任何指点...

跟进:

这并不理想: 如上所述,为了让 Swift 使用 Core Data 模型,你需要用你的应用程序的名称来装饰类名。这适用于构建应用程序,但测试以不同的应用程序名称运行!这意味着您需要进入数据建模器并将该类名称从 myAppname.myEntity 更改为 myAppnameTests.myEntity,然后才能在测试使用或调用时按名称使用这些实体。

【问题讨论】:

尝试用@objc(MyClassName) 修饰类名。这将绕过正常的名称修改。那么您不必担心数据模型中的模块名称。 如果我用 swift 编写实体类,然后使用 objc(xxx) 装饰它们 - 每当我尝试在其他 swift 代码中使用具有该名称的实体时,我都会收到动态转换错误...令人沮丧。 .. 试试这个:***.com/a/26568813/438063 我已经在这个问题中尝试了“跟进”,但它似乎在 Xcode 6.3.1 中不起作用。 Xcode 6.3.1 中有解决方案吗? 您好,我在使用 swift 4 的 xcode 9 中遇到了同样的问题,您解决了这个问题吗? 【参考方案1】:

您完全正确,问题是当您运行应用程序时它正在寻找myAppname.myEntity,而当您以测试方式运行时它正在寻找myAppnameTests.myEntity。我目前使用的解决方案(Xcode 6.1)是不在 CoreData UI 中填写 Class 字段,而是在代码中完成。

此代码将检测您是否作为 App vs Tests 运行并使用正确的模块名称并更新 managedObjectClassName

lazy var managedObjectModel: NSManagedObjectModel = 
    // The managed object model for the application. This property is not optional...
    let modelURL = NSBundle.mainBundle().URLForResource("Streak", 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) ? "StreakTests" : "Streak"

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

【讨论】:

如果您希望您的模块名称是动态的(而不是硬编码),请使用NSBundle.mainBundle().infoDictionary?["CFBundleName"]!。它应该返回“Streak”【参考方案2】:

您需要在 Entity.swift 文件中添加一行,使其也成为像这样的 Objective-C 类:

@objc(YourEntity)
class YourEntity: NSManagedObject 
    ...

如果您的项目不包含任何 Objective-c 代码,我认为这是一个错误。但是,您需要添加该行直到修复此问题。

我从这里学到的。

Youtube video at 11:45

【讨论】:

我的项目有客观的 c 代码 - 但我遇到的问题是 swift 代码正在调用(或尝试调用)Swift Core Data Code... :( 所以你可以试试我的方法。这个对我有用。在我的方法中,您不需要将任何应用名称人员添加到实体。只需保留它的类名即可。【参考方案3】:

这是一个对我真正有用的更新答案。使用 XCode 9.2

Ok, I figured it out! It's not exactly intuitive though. First, I had to comment out the @objc(EntityName) line in the NSManagedObject subclasses. Then I went back to the default Module defined in the xcdatamodeld (Current Project Module). Then I had to ensure that only my test classes (and NONE of the NSManagedObject subclasses or any other source files) were compiled as part of my test project (this was critical) and @testable import AppName in my test project class files. Now it never trys to use these classes in the AppNameTests module, only AppName and the test projects can only call public or internal code...therefore all is right in the universe, yay!

【讨论】:

以上是关于测试与非测试中的 Swift 和 CoreData Casting 问题的主要内容,如果未能解决你的问题,请参考以下文章

使用 Swift 和 Obj-C 进行单元测试

Swift CoreData NSManagedObject 子类

Xcode 中 async 单元测试(UnitTest )方法涉及 CoreData 数据操作测试会失败的原因及解决

swift 3 Xcode 8 中的 NSManagedObject 和 CoreData

如何使用 Swift 删除 coredata 中的特定记录?

CoreData 中的 Swift 数组