Tag: 单元测试

当存在具有相同名称的全局函数时,请小心Swift Extension

在Swift中实现Object Seam时的问题 要认识的基本问题是,当我们在面向对象的程序中查看调用时,它没有定义实际要执行的方法。 现在,您具有以下代码。 一个类调用全局函数。 现在,您要实现对象接缝。 以下实现将产生完全不同的结果。 B中的扩展是单元测试模块,而A中的扩展是其原始模块。 当您运行它时,在其他模块中实现的Swift扩展在动态调度中将具有较低的优先级。

如何在Clean-Swift中编写单元测试

在 第1部分中, 我解释了如何改善应用程序的体系结构,使其更好地进行单元测试。 编写测试应该和编写代码一样有趣,对吗? 但是为什么不呢? 这就是我问自己的问题,我注意到每次尝试编写一些测试时,我都不知道从哪里开始或要测试什么。 而且,如果我有一个主意,结果证明我无法测试或不知道如何测试。 一段时间后,我开始意识到,如果您的代码结构清晰,那么您将知道在代码中放置什么内容,并且可以清楚地测试什么。 在第1部分中,您可以阅读如何根据Clean Swift模式构造代码。 在这一部分中,您可以阅读可以测试的内容以及如何进行测试。 通常,好的测试是灵活的,但并不脆弱,这意味着,如果更改一行代码,则不应破坏很多测试。 您的测试应该广泛,涵盖所有边缘情况并快速运行。 因此,您可以经常运行它们并尽快获得反馈。 单元测试的结构 在测试中,您可以区分要测试的主题 , 输入和输出 。 最好确定这三个不同的部分,因为它们可以帮助您弄清楚您需要如何处理它们。 例如,您永远不会存根您的测试主题,但可以存根 。 您将需要尽可能多的输入集测试以测试其结果。 并且在测试的断言中验证输出的值。 单元测试的典型结构是: 给定 什么时候 然后 在“ 给定中,您将创建模拟并准备输入”中,在“执行方法以对主题进行测试时”中, 然后在“使用声明中验证输出”。 不同类型的测试双打 为了为我们的测试创建真正的黑匣子,我们可能希望掩盖实现的某些部分。 我们可以使用所谓的测试双打来做到这一点。 当您必须在测试中传递参数时,但是您知道它永远不会在被测试的方法中使用时,可以传递一个哑元 。 假人返回什么都无所谓。 当您需要一个返回特定值的虚拟对象,因为系统的其余部分依靠它继续运行测试时,该虚拟对象称为stub 。 如果仅想测试是否调用了方法,则可以使用间谍 。 不利的一面是,您监视的内容越多,您将测试与应用程序实现的耦合就越紧密。 模拟是其中包含断言的间谍。 它检查使用什么参数,何时以及多久调用一次函数。 我们最后的味道是假的 。 到目前为止,所有讨论过的测试倍数都不关心您传入的参数。但是假冒的确如此,因为它具有业务逻辑,可以为不同的输入提供不同的输出。 将单元测试文件添加到您的项目 要将单元测试文件添加到Xcode项目中,可以在新项目开始时检查单元测试,也可以稍后将测试目标添加到现有项目中。 在项目浏览器中单击您的项目 ,添加一个新目标并选择一个单元测试包 : 在文件顶部,您需要import XCTest并通过添加@testable import […]

伪造XCTest中Firebase实时数据库API的响应

我在工作中使用Firebase实时数据库,当我在寻找有关模拟使用Firebase实时数据库的单元测试时,发现了这篇文章(用日语编写)。 Firebase实时数据库をiOSで扱う际の模拟について考えてみた 松。松馆(@d_date)です。 medium.com 还写到,不仅仅是模拟实时数据库,而且它是如此有用。 这次,我试图考虑一种更简单的方法来模拟XCTest中的实时数据库的响应。 希望对不想做复杂事情的人有所帮助。 这个应用程式程式码的作用如下: 正在将DatabaseRefenrece的实例传递给构造函数。 使用observeSingleEvent API一次获取“样本”路径的数据。 对响应中的快照进行一些操作,然后将结果传递给完成模块。 该应用程序的所有单元测试代码如下所示。 让我们一一看。 让我们从测试用例开始。 在第一行创建目标类的实例。 那时,将MockDatabaseReference(它是DatabaseReference类的模拟对象)作为构造函数参数传递。 稍后我将解释该模拟类。 此外,请使用XCTestExpectation,因为从实时数据库获取数据应该是异步操作。 这是之前提到的DatabaseReference类的模拟对象。 这次,应用程序代码使用了observeSingleEvent API,因此覆盖了该功能以及子功能。 覆盖子函数的原因是,如果应用程序代码使用子 API来请求特定的路径数据,则该API返回另一个新的DatabaseReference实例,那么我们就无法模拟响应。 因此,这一次,覆盖子 API并返回模拟对象本身,以便应用程序代码在实现模拟时可以请求任何路径。 如果要更改响应数据取决于请求的路径,则可以通过重写子 API并为每个路径返回DatabaseReference的不同模拟对象,来做到这一点,如下所示: 最后,重写DataSnapshot类,它是实际的响应数据。 DataSnapshot的value属性最初是只读属性,因此我们无法使其令人满意。 因此,重写它并返回我们要用作测试数据的数据。 通过在MockDatabaseReference类的observeSingleEvent中使用此MockSnapshot对象,我们最终可以使Firebase Realtime Database API根据需要返回数据。

设置Clean Swift模式有助于编写单元测试

您是否知道这些时刻可以启动应用程序,说服自己需要通过编写尽可能多的单元测试来证明质量? 在那之前,您发现几乎不可能或花费大量时间编写适当的单元测试。 好了,我有了这些时刻,并开始意识到,您的应用程序中的良好模式有助于编写单元测试。 好的模式是第一位的 为了编写更好的单元测试,您需要一个一致的模式。 如果您想编写更好的单元测试,那么MVC模式绝对不是最佳模式。 大多数情况下,您会得到一个非常大的ViewController ,其中包含各种工作:Massive View Controller。 在良好的模式下,每个组件都充当独立于任何其他组件的黑匣子。 黑匣子里面发生了什么,您不知道也不在乎。 但是这些黑匣子应具有定义明确的输入和输出。 在一致的结构中,应该更容易识别组件,测试主题,输入和输出。 代码独立性是通过在Swift中使用协议来实现的。 通过使用协议,一个组件不会直接拥有另一个组件,而是通过协议间接引用了该组件。 这样,这两个组件不会互相了解,但是都依赖于相同的协议。 在将数据从一个组件发送到另一个组件时,模型结构可以在Swift中提供数据独立性。 通过为数据请求,数据响应和视图模型创建单独的模型,可以避免在更改一个模型时还必须更改将模型用作输入或输出的对象以外的组件。 这也适用于他们的测试,这使这些测试不那么脆弱。 我遇到的一个不错的模式是使用VIP循环的Clean-Swift 。 在这种模式下,一切都会通过一个VIP循环朝一个方向前进。 它从ViewController开始,到Interactor ,再到Presenter ,最后回到ViewController 。 两个组件通过包含输出和输入逻辑的协议相互通信。 ViewController处理显示逻辑, Interactor业务逻辑和Presenter表示逻辑。 对于单元测试,这意味着您可以在每个边界上测试所有协议方法,因此它将完全覆盖所有组件。 哪里去了? 开始时,可​​能不清楚在代码中的什么位置。 为了更好地理解我们所追求的模式的实现,我将举一个例子。 在此示例中,我在UITableViewController中显示电影标题及其发行年份的列表。 电影的模型如下所示: 结构电影{ 让标题:字符串 让releaseDate:字符串 } 首先,我创建一个具有Interactable- , Presentable-和Displayable协议的ListMoviesViewController , ListMoviesInteractor和ListMoviesPresenter 。 如果您开始编写应用程序代码,则最简单的方法是先从ViewController , Interactor和Presenter开始 。 开始编写单元测试时,最简单的方法是从Interactor , Presenter和ViewController开始 。 在VIP模式中, […]

不要引入副作用

最好的情况是,单元测试是一种有用的诊断工具,可帮助您了解应用程序的哪些部分正常工作,哪些无效 。 单元测试可帮助您找到回归 ,由于最近的一些代码更改,这些功能不再起作用。 有了完善的测试套件,您可以精确地确定哪些代码行导致了最近的回归,并且可以对其进行修复。 但是,在最坏的情况下,单元测试带来的混乱比清除起来要多。 您可能不知道哪些测试真正通过了,因此,您可能不再认为值得再运行这些测试了。 到那时,测试花费的时间超过了您节省的时间。 发生这种情况的一种常见方式是单元测试产生副作用时。 在本文中,您将探索如何发生这种情况以及如何避免这种情况。 什么是副作用? 副作用是由于运行测试而导致的一些长期后果 。 副作用可能影响应用程序本身,测试套件的安装,或者在更极端的情况下,影响应用程序所依赖的服务器环境。 在大多数情况下,副作用只会造成混乱。 由于副作用似乎是随机的,因此您最终可能会创建难以跟踪和重现的错误 。 这可能使您陷入一些奇怪的兔子洞,从而调查与应用程序逻辑无关的问题。 让我们研究一下测试可以产生副作用的方式。 本地存储的副作用 单元测试绝不应向文件系统写入任何内容 。 如果这样做,它们可能会影响您的应用安装所依赖的存储状态。 当这种情况发生时,这不是世界末日。 这不是那种会影响客户电话(基本上是从App Store下载它的人)在生产中使用该应用程序的事情。 话虽如此,它可能会导致测试设备上出现一些奇怪的错误。 让我们再来看上一篇文章中的AccessGranter类,不要依赖临时状态。 在该文章中, AccessGranter授予访问应用程序中功能的权限,但前提是用户已登录。该类的第一个版本直接由用户默认值确定。 这是从该文章复制的相同代码。 即使您不是api开发人员,Postman还是一个不错的工具。 您可以实时了解api团队的进展情况。 我过去常常将所有时间都花在编写会访问api团队正在研究的不同端点的代码上。 为了测试这些端点是否正常工作,我将手动运行该应用程序。 邮差通过为我完成所有这些工作来节省时间。 在将应用程序连接到服务器之前,我可以快速检查Postman。 如果连接处于活动状态,并且请求返回了预期的数据,那么就可以开始了! 下一步是什么? 具有较小的全局可变状态的副作用是较少的副作用 。 当编写更好的代码时,最终会编写出更有效的测试。 当您专注于编写更有效的测试时,您会编写更好的代码。 这就是单元测试可以使您成为更好的开发人员的方式。 我目前正在写有关iOS单元测试的书。 下一篇文章将重点介绍编写快速的单元测试。 同时,您可以查看有关单元测试的其他工作。 不要依赖临时状态 构建iOS单元测试的四个简单规则 我们应该在iOS应用程序中进行哪些单元测试?

单元测试。 开始。

你是一个伟大的程序员。 至少我希望如此。 即使您遵循SOLID,DRY和KISS原则编写了干净的代码,您的应用程序也会出现错误。 显然是因为算法。 您可以为新的数据传输算法创建出色的逻辑,但是如果在代码中实现它时犯了一些错误,它将无法正常工作。 单位代码使您可以自动测试代码的逻辑。 您需要编写一个测试,并且该测试将在每次对代码进行任何更改时运行。 让我们编码吧! 哦停下来 下载我为您制作的启动项目。 https://yadi.sk/d/2qbUCjKJNRAccQ 只有一个包含逻辑的类,我们将对其进行测试。 导入Foundationclass数学:NSObject {静态函数add(value1:Int,value2:Int)-> Int { 返回值1 +值2 } 静态函数减法(value1:Int,value2:Int)-> Int { 返回值1 —值2 } 静态函数乘法(value1:Int,value2:Int)-> Int { 返回值1 *值2 } 静态函数Divide(value1:Int,value2:Int)-> Int { 返回值1 /值2 } } 打开测试导航器,然后单击+ 选择“新单元测试目标”。 您将看到从XCTestCase继承的新创建的类。 导入XCTestclass UnitTestsStarterTests:XCTestCase { 覆盖func setUp(){ super.setUp() } 覆盖func tearDown(){ super.tearDown() } func testExample(){ } […]

如何在iOS中将一个测试目标文件用于两个应用目标?

我搜索了许多解决此问题的方法之后写这篇文章。 但是我找不到解决我问题的合适方法。 实际上,问题是“我的应用程序中有两个目标分别为SampleB2B和SampleB2C 。 这两个目标相似,只是变化不大。 因此,当我尝试为两个目标编写XCTestCase时,我认为两个目标都有一个测试目标文件。 因为所有测试文件都是相似的。 因此,考虑为两个目标编写一个测试文件。 但是我找不到一种对两个目标使用相同的测试文件的方法。” 这是我在项目中使用测试用例文件的方法,以同时将测试用例文件用于两个目标。 请按部就班。 最初,我只有一个目标,后来又改变了要求。 因此,我需要再创建一个目标。 因此,我只是从现有目标复制来创建另一个目标,然后重命名以区分这两个目标。 完成后,将类似于下面的快照。 我有两个上述目标,有一个测试目标是在创建项目时创建的。 现在,我在“ SampleTests ”文件夹中有一个测试用例文件。 所以我需要使用相同的文件来测试两个目标。 所以我做了以下。 因此,现在,我们需要从上面的窗口中再创建一个测试目标。 不要从“文件”->“目标”创建测试目标。 如果要从中创建,则它将为新创建的创建测试目标文件夹。 实际上,在开始测试时,测试文件需要位于同一测试目标文件夹中。 这导致在两个测试目标中创建重复文件。 两个测试目标文件夹下的两个文件中的文件内容相同。 我想以更好的方式做到这一点。 因此,我尝试了其他任何方法来将相同文件用于两个测试目标。 因此,右键单击现有的目标SampleTests,然后单击重复。 然后重命名测试目标,以区分并重命名所有目标的Info.plist,并在“ 对应目标”->“构建设置”->“ Info.plist”文件名中提供相应的Info.plist文件路径。 一旦完成,它将像 现在,我有两个目标,其中两个是测试目标,一个是测试文件夹和测试文件。 2.为两个测试目标选择相应的主机应用程序,如下面的快照。 3.在两个测试目标的活动编译标志中添加标志,以在运行时区分当前目标。 选择测试目标->构建设置->活动编译条件,如下面的快照。 在B2B测试目标中添加B2B标志,在B2C测试目标中添加B2C标志。 它将在步骤5中使用。 4.在添加或创建新的测试用例文件时,请确保为两个测试目标都添加了文件。 也检查现有文件的目标成员资格 。 5.在测试文件顶部添加以下代码。 导入项目模块取决于当前的目标。 #if B2B @testable导入SampleB2B #其他 @testable导入SampleB2C #万一 如果要基于目标更改测试用例文件,我们可以使用同一段代码来区分。 6.单击目标和编辑方案,然后添加测试目标,如下所示。 对两个目标都执行此步骤。 为相应的应用目标添加相应的测试目标。 […]

XCTest样本

在本教程中,您将尝试执行以下测试操作: 在视图控制器中测试方法。 检查视图是否存在。 在当前状态下捕获屏幕截图 如果您遇到以下任何问题,可以在此处下载完整的源代码项目。 1.在视图控制器中测试方法 首先,您需要阅读以下来自Apple的文章: XC测试 为Xcode项目创建并运行单元测试,性能测试和UI测试。 定义测试用例和测试方法 将测试用例和测试方法添加到测试目标,以确认您的代码行为符合预期。 了解测试方法的设置和拆卸 测试运行前准备初始状态,测试完成后执行清理。 addTeardownBlock(_ 🙂 注册一个拆卸代码块,以在当前测试方法结束后运行。 测试期望的异步操作 验证异步操作的行为符合预期。 使用2个选项创建一个新项目,其中包括“包括单元测试”和“包括UI测试”已选中: 要在视图控制器中测试方法,您需要对其进行初始化。 在这种情况下,我们将通过UIStoryboard对其进行初始化。 将以下代码片段添加到您的DemoTests.swift文件中: 现在,您可以调用视图控制器的方法: 按Cmd + U运行测试 2.检查视图的存在 首先,您需要阅读以下来自Apple的文章: 用户界面测试 当执行预期的操作时,请确保您应用的用户界面行为正确。 XCUIElementQuery 用于查找UI元素的查询。 XCUI元素 应用程序中的UI元素。 XCUI应用 可以启动和终止的应用程序的代理。 首先,要能够测试UI元素,您需要启动您的应用程序。 将以下代码片段添加到您的DemoUITests.swift文件中 现在,您可以使用app变量查询UI元素: 按Cmd + U运行测试 3.在当前状态下捕获屏幕截图 首先,您需要阅读以下来自Apple的文章: 将测试分为具有活动的子步骤 在测试方法中创建命名活动以简化测试报告。 为测试和活动添加附件 使用附件存储测试的输出数据以供以后分析。 XCUI画面 连接到设备的物理屏幕。 XCUI截屏 屏幕,应用程序或UI元素状态的捕获图像。 要捕获当前状态的屏幕快照,可以使用以下代码片段: 按Cmd + […]

用继承编写可测试和可维护的代码

总览 软件工程师应努力实现的众多目标之一是构建可测试且可维护的代码,并且似乎许多人无法编写此类代码。 我敢肯定有很多原因会导致这一因素,例如完成项目的紧迫期限,或者工程师可能没有学习编写代码的正确方法。 我在Kaodim工作,我很高兴能有机会向我的前辈学习,并能够理解为什么我们倾向于以某种方式做事。 我记得在Kaodim编码我的第一个功能时,我在很多方面都感到不足,因此开始编写意大利面条式代码。 当我重新审视我的第一个功能时,我开始注意到我以前犯过的错误,并且我觉得自己可以做得更好。 供您参考,我的第一个功能称为“客户注册”,它允许新用户注册,而现有用户可以使用OTP功能登录。 这是该功能的技巧,但是随着我们深入研究,围绕它展开的逻辑很多。 对于这项功能,八月,我的上司创建了一个一次性密码库,我可以不时地对其进行修改,以适应该功能的需求。 代码气候测量 我们使用Code Climate来衡量我们的代码质量和可维护性。 在重构代码之前,我的PhoneNumberViewController和VerifyPhoneViewController的质量进行了如下测量: 继承方法 这里的计划是专门使用面向对象的编程继承,并且通过这种技术,我能够编写可伸缩且可测试的代码。 让我直观地向您介绍我们预期的代码结构。 为了让您更清楚地了解情况,我有两个父类,其子类数目灵活。 每个子类都将从父类继承,这样我就可以消除子类中的重复代码,并可以添加所需的任意多个子类。 让我以可视化的方式向您展示屏幕,然后给您一个场景。 作为新用户,我输入了我的电话号码,并被提示使用OTP代码进行注册。 然后,我通过短信收到我的OTP代码,并将其输入到我的验证屏幕上。 通过这种方法,我能够最大程度地减少代码中的冗余并拥有更简洁的代码结构。 但是,作为工程师,通过查看代码可以清楚得多。 我们将首先查看父类,在该类中,后端工程师创建了一个API,以检查电话号码以确定是否需要验证电话。 在满足不同条件的情况下,执行了不同的函数,但就我而言,我创建了几个空函数,这些空函数可以被子类覆盖。 比以前的分数高两个等级。 但是从可维护性来看,持续时间从24小时分别提高到25分钟和1小时有很大的提高。 我认为这是一个巨大的进步。 但是,我觉得我们仍然可以做得更好。 随之而来的是,所有子类创建的可维护性得分为A,0分钟。 快速而灵活地编写单元测试 我们将使用Quick&Nimble进行单元测试。 首先,我们将模拟自己的数据,而不依赖于API调用。 我们可以这样做,如下所示: 在这里,我们正在创建自己的模拟数据版本,以确定每个键的值,然后可以选择所需的返回值。 接下来,一旦有了模拟数据,就可以进行相应的测试。 首先,我们将测试电话是否被拿走,我们不允许用户注册。 我们可以通过选择checkNumberCase以返回我们需要的某些值来进行检查。 像往常一样,没有我的上校奥古斯特的帮助,我一个人不可能提出这种解决方案。 他在提出减少重复代码的解决方案方面做得非常出色,我得以在他的指导下实施该解决方案。 最后,我相信有很多不同的方法可以实现这一目标。 但是,我发现它可以以我的方式很好地工作,如果您以任何方式认为可以完成任何改进,请在下面随意评论。

code + S的Xcode单元测试

使用Xcode IDE进行TDD可能会造成破坏。 每次在实现或测试文件中进行小的更改时,Xcode都会重建整个项目(幸运的是使用增量构建),并将该应用程序安装在模拟器上。 根据您拥有的项目配置,可能需要几秒钟到几十秒的时间。 本文提供了一种方法,可以在您保存实施.swift文件时绕过该持久过程并自动运行单元测试。 我们将利用John Holdsworth的“ Injection for Xcode”应用程序的新功能-支持自动测试。 TLDR; InjectionTDD(与InjectionForXcode一起)在模拟器环境上运行单元测试,而无需重建或重新安装应用程序。 实施和测试已注入到已经运行的主机应用程序中,因此几乎可以在标准的知名Xcode IDE中实时运行。 另外,为了加快测试过程,InjectionTDD仅运行一部分测试用例,这些用例与刚更新的实现文件有关。 如果您正在进行TDD测试,则要获得快速反馈,无论测试是通过(绿色)还是失败(红色),这一点至关重要,但不仅如此。 立刻知道您的更改/修复不会在其他地方引入任何回归会不会很好? 还是您可能要用新的测试用例来覆盖实现,而重新构建测试目标就变得很沮丧? 如果您发现相同的问题,InjectionTDD可以节省大量时间! 通常,要运行单元测试,您必须等待几个步骤,例如:构建,链接,安装整个应用程序以及将Xcode与lldb服务器连接。 根据您的配置,最多可能需要数十秒钟。 但是,在开发过程中,我们经常只修改一个特定文件,然后观察结果。 InjectionForXcode通过实时编译更新的文件并在实时iOS流程中替换/替换其实现方式来节省大量时间。 非常相似的方法可以应用于单元测试。 通常,这就是InjectionForXcode + InjectionTDD工作方式: 首先,Xcode构建您的测试目标,在模拟器上安装应用程序,然后尝试执行第一个测试。 但是,必须将专用框架称为InjectionTDD集成到测试目标中,使测试执行停止,并使运行时保持恒定的等待循环。 然后,每当您在Xcode中进行某些更改时,InjectionForXcode都会构建应用程序的一部分(仅编辑文件+相关的单元测试),并将其注入已经等待的“托管应用程序”中。 最后,主机应用程序执行所有测试,并将结果传递回Xcode,以将其呈现在其标准UI中。 请记住,主机应用程序此后不会终止,可以重复整个过程。 拥有理论背景,让我们卷起袖子,玩转它。 您可以使用以Swift编写的带有单元测试的任何现有项目开始,也可以通过pods命令pod try InjectionTDD : 在进行任何工作之前,请确保终端命令xcode-select -p指向当前的Xcode版本路径。 如果不是,请使用-s选项分配正确的版本,例如: sudo xcode-select -s /Applications/YOUR_XCODE.app/Contents/Developer 从官方站点安装免费的“ Injection for Xcode”应用程序(请记住安装具有TDD支持的版本)并运行它。 请注意 ,应用程序仅位于纸盘中。 将InjectionTDD框架集成到您的测试目标(支持CocoaPods,Carthage和手动安装)。 如果您使用CocoaPods,则只需一行更改: 目标“ YourAppTests” pod’InjectionTDD’,’〜> […]