破解核心数据测试

核心数据是一个框架,它隐藏诸如对象生命周期和对象图管理之类的持久层的逻辑,以帮助您以高级方式管理模型层对象。 是的,Core Data对许多开发人员来说都是有争议的。 但是,它仍然是有用的框架:性能良好,并且得到Apple的支持。

关于核心数据的单元测试,有很多不错的文章,但是大多数文章集中在模拟上下文。 由于发布了持久性容器类NSPersistentContainer,因此越来越难以模拟上下文并将测试写入容器。 因此,在本文中,我将探讨一个新主题:为NSPersistentContainer编写测试,增强型核心数据。

我们将一起使用NSPersistentContainer设置核心数据堆栈,并为此编写测试。

TL; DR

我们将学习如何:

  • 使用NSPersistentContainer构建核心数据栈
  • 使用内存永久存储
  • 引入两个测试双打:“假”和“存根”
  • 做一个异步测试

先决条件

  • 迅捷3
  • Xcode 8
  • 核心数据基础知识
  • iOS 10(由于我们将使用NSPersistentContainer,因此需要iOS 10)。

以下是一些有关核心数据的好文章:

  • 完整的核心数据应用程序(代码在Objective-C中,但是概念相同)
  • 核心数据入门教程

我们的任务

现在,我们有一个简单的任务:实现待办事项列表系统,在其中我们可以创建,读取,更新和删除待办事项(CRUD)。 而且这个待办事项系统应该永久保存数据,以便即使我们终止应用程序也可以稍后获取它们。

因此,我们需要实现这些方法:

  1. 创建待办事项
  2. 提取所有待办事项
  3. 删除待办事项
  4. 提交对永久存储的更改

由于我们只想在需要时执行保存,因此我们不会在创建/编辑/删除方法中将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测试。 忍受我,我们正在实现我们的目标!