如何在 Swift 中正确测试核心数据

How to test Core Data properly in Swift

已经有很多关于这个的主题,但我还没有找到适用于 Swift (Xcode 6.2) 的解决方案。

为了在 Swift 中测试支持 classes 的核心数据,我生成了新的托管对象上下文,然后将其注入到我的 classes 中。

//Given   
let testManagedObjectContext = CoreDataTestComposer.setUpInMemoryManagedObjectContext()
let testItems = createFixtureData(testManagedObjectContext) as [TestItem]
self.itemDateCoordinator.managedObjectContext = testManagedObjectContext

//When
let data = self.itemDateCoordinator.do()

//Then
XCTAssert(data.exists)

问题来自将测试中创建的 MOC 传递给正在执行的 class。因为实体 classes 是命名空间的,所以 Core Data 不会获取您适当的 ManagedObject subclass,而是返回一个 NSManagedObject 集合。当对这些对象进行循环或做任何事情时(在您的 class 中将是一组测试项 ([TestItem])。

例如,有问题的 class ItemDateCoordinator 将执行此循环(在从 NSFetchRequest 中提取相关数据后)"

for testItem in testItems {
    testItem.doPart(numberOfDays: 10)
}

会导致:

fatal error: NSArray element failed to match the Swift Array Element type

此外,我遇到了一些没有太多可靠答案的信息:

我正要向您指出 Swift、核心数据和单元测试,但您已经找到了。 :)

post 没有详细说明文件应该存在的位置(即,在哪个 Target 中)。您不应NSManagedObject子类(或任何文件)添加到两个目标。我发现这会导致各种难以发现的错误和神秘错误。

并且绝对不要this。这是一个可怕的黑客攻击。

相反,在 XCTestCase 文件中创建 类 public 和 import MyAppTarget。更好的是,正如我在最近的 talk 中提到的那样,您的模型应该在自己的框架中(视频将在几周内 post 在 realm.io 上编辑)。这样做会使您的模型命名空间非常清晰并且通常更易于处理。然后,您需要 import MyAppModel 在访问托管对象的任何地方。

我还有一个新框架,JSQCoreDataKit,旨在让 Core Data 在 Swift 中更易于使用。该框架的一个关键部分是 CoreDataStack ,您可以使用内存中的存储对其进行初始化以进行测试。有带示例的演示应用程序,以及评论良好的单元测试。

我相信这是最近更新的 (iOS 9/Swift 2.0) 在导入的目标上有 testable 关键字,意味着目标的内部 类(默认)变成 public。来自文档:

所以要添加到上面的 jessesquires 答案中,请将 @testable 附加到您的导入中,这应该可以解决单元测试错误:

@testable import MyAppTarget