如何在 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
此外,我遇到了一些没有太多可靠答案的信息:
- 要在创建实体时投射实体,我一直在使用 Jesse 的解决方案,但这不适用于更大范围的测试。
- A solution has been posted on another question 涉及在运行时换出 classes,但这对实体继承不起作用。
- 在这种情况下,是否有另一种方法可以使用 Core Data 测试您的对象?你怎么做呢?
我正要向您指出 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
已经有很多关于这个的主题,但我还没有找到适用于 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
此外,我遇到了一些没有太多可靠答案的信息:
- 要在创建实体时投射实体,我一直在使用 Jesse 的解决方案,但这不适用于更大范围的测试。
- A solution has been posted on another question 涉及在运行时换出 classes,但这对实体继承不起作用。
- 在这种情况下,是否有另一种方法可以使用 Core Data 测试您的对象?你怎么做呢?
我正要向您指出 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