破解核心数据测试
核心数据是一个框架,它隐藏诸如对象生命周期和对象图管理之类的持久层的逻辑,以帮助您以高级方式管理模型层对象。 是的,Core Data对许多开发人员来说都是有争议的。 但是,它仍然是有用的框架:性能良好,并且得到Apple的支持。
关于核心数据的单元测试,有很多不错的文章,但是大多数文章集中在模拟上下文。 由于发布了持久性容器类NSPersistentContainer,因此越来越难以模拟上下文并将测试写入容器。 因此,在本文中,我将探讨一个新主题:为NSPersistentContainer编写测试,增强型核心数据。
我们将一起使用NSPersistentContainer设置核心数据堆栈,并为此编写测试。
TL; DR
我们将学习如何:
- 使用NSPersistentContainer构建核心数据栈
- 使用内存永久存储
- 引入两个测试双打:“假”和“存根”
- 做一个异步测试
先决条件
- 迅捷3
- Xcode 8
- 核心数据基础知识
- iOS 10(由于我们将使用NSPersistentContainer,因此需要iOS 10)。
以下是一些有关核心数据的好文章:
- 完整的核心数据应用程序(代码在Objective-C中,但是概念相同)
- 核心数据入门教程
我们的任务
现在,我们有一个简单的任务:实现待办事项列表系统,在其中我们可以创建,读取,更新和删除待办事项(CRUD)。 而且这个待办事项系统应该永久保存数据,以便即使我们终止应用程序也可以稍后获取它们。
因此,我们需要实现这些方法:
- 创建待办事项
- 提取所有待办事项
- 删除待办事项
- 提交对永久存储的更改
由于我们只想在需要时执行保存,因此我们不会在创建/编辑/删除方法中将NSManagedObjectContext .save()视为副作用。 相反,我们将其设为独立方法。 当用户按下“保存”时将允许保存数据,或者当用户离开当前视图时将其保存。
尽管通过使用NSPersistentContainer在iOS 10之后设置Core Data堆栈更容易,但最好具有一些基本知识。 我们将在以下各节中采用这些概念。 因此,在下一节中,我将简要介绍Core Data堆栈。
核心数据栈
根据苹果公司的说法,核心数据栈是框架对象的集合,这些对象是核心数据初始化过程的一部分。
核心数据堆栈中包含4个基本组件:
- 托管对象上下文 (NSManagedObjectContext):为托管对象提供便签本
- 持久性商店协调器 (NSPersistentStoreCoordinator):汇总所有商店
- 托管对象模型 (NSManagedObjectModel):描述商店中的实体
- 持久对象存储 :包含已保存的记录。
在本文中,我们主要关注用于并发的托管对象上下文 ,用于内存持久存储的持久存储,以及用于在生产目标和测试目标之间共享实体描述的托管对象模型 。
设置Core Data堆栈时曾经有很多样板,但是在iOS 10之后变得更加简单。我们可以使用NSPersistentContainer用几行代码封装整个Core Data堆栈。
环境
现在让我们更进一步,设置核心数据堆栈。 如果您在启动项目时选择了“使用核心数据”选项,那么您已经准备就绪。 如果没有,请将以下代码段添加到AppDelegate来设置全局容器:
设计一个TodoStorageManager
TodoStorageManager是一个类,负责与核心数据的交互。 在此管理器类的帮助下,我们可以专注于其他业务逻辑,而不会受到存储逻辑的干扰,并使存储代码可重用。
因为它是一个主要与Core Data交互的类,所以毫无疑问,此类的依赖关系是持久性容器。
这是ToDoStorageManager的初始化:
然后,我们要在后台线程而不是主线程中提交更改,因此我们设置了一个方便的backgroundContext :
最后,这是CRUD和保存逻辑的实现:
现在,我们有了一个简单的管理器,它能够创建删除提取待办事项并将更改提交到持久性存储中。
测试设置
在本节中,我们将编写测试代码。 我们从设置SUT开始:
我们初始化一个TodoStorageManager并注入一个持久性容器mockPersistentContainer作为依赖项。
在以下两节中,我们要介绍两种测试双打,Fake和Stub,以帮助我们编写Core Data测试。 忍受我,我们正在实现我们的目标!