如何在iOS上使用伪数据进行测试

为了提供高质量的软件并避免退化,对于每个iOS应用程序都必须实施单元测试。

模拟对象是单元测试中的一种技术,它通过使用与真实API相同的API来创建伪造对象。

本文旨在为您提供有关如何使用假数据以及为iOS应用程序上最常出现的场景编写单元测试的最佳实践。

在编写单元测试时,我们应始终避免更改应用程序目标的真实数据,而应仅将伪造的数据用于测试目的。

以下部分将讨论如何通过使用常用iOS API的伪造数据编写测试。

在软件开发中,减少对象的依赖性始终是一个好习惯。 最佳情况下的依赖关系应注入使用它们的类中。

但是,如果我们检查现实生活中的iOS开发场景,几乎每个项目UserDefaults通过直接调用其API来存储或检索任何数据来使用UserDefaults

因此,我们将尝试提供一种测试UserDefaults的实用解决方案,而不是通过协议抽象其API。

我们可以在UserDefaults上创建两个新函数

始终不改变单元测试目标的应用程序数据是一个好习惯,因此,我们应该为测试目标创建另一个保存用户数据的地方。

在这种情况下,我们使用suiteName — testDefaults初始化一个新的UserDefaults对象,它完全独立于标准UserDefaults

让我们尝试编写一个使用UserDefaults的简单测试

由于这些数据基本上仅用于测试,因此我们应避免将这些数据挂在我们的应用程序文件中,因此,我们在完成测试后创建了一个负责删除此存储的函数。

当然,清除此数据的最佳位置是我们的单元测试类中的tearDown函数。

Singletons对象在iOS上的许多API上得到了广泛使用,我们可以在NSFileManagerNSApplicationUIApplication以及其他许多地方找到它们。

对于iOS开发人员而言,了解如何测试单例是一件有用的事情。

在我们的示例中,我们将使用apple的iAd框架。 我们将创建一个文件以获取本地JSON响应,而不是请求广告属性详细信息时的真实数据。

iOS中的一个不错的功能是,迅速的扩展使我们不仅可以为预定义的API添加新功能,还可以使它们符合我们自己的自定义协议。

让我们定义一个AdvertismentClient协议

之后,默认情况下,我们通过默认的ADClient和我们的虚假广告客户端都遵守此协议,如下所示

然后,我们将依赖关系更改为

 私人var adClient:AdvertismentClient = ADClient.shared() 

要么

 私人var adClient:AdvertismentClient = MockAdClient() 

并如下使用

通过这种方式,我们可以轻松决定何时使用真实数据以及何时进行测试,这取决于我们是进行单元测试还是从实时应用程序目标中调用API。

核心数据在iOS中仍非常用于缓存数据。 测试核心数据实体可能很棘手。 下面我们将解释组织核心数据服务和伪造数据的良好实践。

通常,在大多数情况下,创建一个负责在数据库中获取和写入特定数据的服务类总是一件好事,而不是在整个项目中使用核心数据代码。

这主要有两个好处:

  • 它使您与正在使用的基础数据库脱钩,如果将来您想用任何其他数据库替换核心数据,则只需在一个类中进行更改。
  • 通过这样做,我们可以轻松确定将使用哪个CoreDataStack或在其他框架中可能需要的任何其他设置。

我们将创建一个CoreDataStack协议,然后创建两个符合该协议的MainCoreDataStack ,一个MockCoreDataStack和一个MockCoreDataStack

然后,取决于我们是在应用程序目标上还是在单元测试目标上使用它,它们中的任何一个都可以初始化我们的DatabaseService。

我们的主要核心数据堆栈将具有如下的默认设置

始终进行单元测试时,在测试它们时,应避免更改当前“真实”对象的状态。

因此,当我们要创建伪造的核心数据实体时,我们应该有一个单独的持久性存储,并使用内存中的存储类型配置,这样所做的更改将不会保存在磁盘上,并且将它们与当前持久性数据完全分开。

现在,我们将能够创建默认情况下使用MainCoreDataStack初始化的数据库服务。

在测试类中,我们可以使用伪造的数据栈对其进行初始化

现在,我们可以编写一些简单的测试,如下所示:

通过使用这种方法,我们可以轻松测试我们的DatabaseService,而不会影响应用程序目标存储的任何数据。

在测试网络层时,我们可以使用面向协议的方法来创建协议,并通过真实的NetworkService和MockNetworkService使其遵循该协议,然后使用真实或模拟的服务注入依赖性。

尽管在本文中,我们将使用一个非常好的开源库OHHTTPStubs,该库将更好地处理模拟和存根。

这个库的好处是,它可以与著名的iOS网络库Alamofire协同工作。

使用OHHTTPStub可以轻松实现网络请求的存根,您可以通过使用字典提供自定义响应来替换特定路径或主机的任何响应。

之后,从应用程序转到以下URL的每个请求都将返回我们的自定义响应。

let tasksURL = URL(string: “https://jsonplaceholder.typicode.com/todos")!

自定义响应的真正好处还在于,您可以通过简单地在响应中返回错误来轻松测试错误和边缘情况是否得到正确处理。

手动构建用于响应的字典是一个很棒的功能,但是当我们想返回带有许多属性的大型JSON数据时,它可能变得凌乱并且很难在我们的测试类中维护。

在这种情况下,我们可以使用JSON文件对响应进行存根,如下所示。

现在,每次我们的应用发送请求时,我们都会从保存在文件中的myResponse.json文件中获取响应。

但是,我们应该记住避免将敏感信息保存在这些JSON文件中,因为如果将这些文件与应用程序一起提供,则可以轻松查看它们。

您可以查看我有关安全性主题的更多文章。

每个iOS应用程序都必须具备应用程序安全性

应用程序安全性是软件开发最重要的方面之一。

medium.com

如果我们要尽可能避免回归并尝试提供无缺陷的应用程序,则必须对应用程序进行单元测试。

在本文中,我们讨论了如何为iOS开发期间发生的常见情况提供测试。

我们讨论了如何测试UserDefaultsSingeltonsCore DataNetwork Requests

如果您喜欢本文,请确保鼓掌以表示支持。

跟随我查看更多文章,这些文章可以使您的iOS开发人员技能更上一层楼。

如果您有任何疑问或意见,请随时在此处留言或发送电子邮件至arlindaliu.dev@gmail.com。