Tag: 单元测试

知道如何进行单元测试的绝佳过程(您的Swift代码)

我将向您展示一种方法,该方法可以确定应测试的内容以及一些概念,使编写测试时的生活更加轻松。 在我职业生涯的某个时刻,我知道单元测试的重要性,但是我不知道如何测试代码。 许多人都面临着同样的问题。 我们阅读了有关如何开始的文章,但仍然很难将我们学到的东西应用到我们自己的代码中。 通常,我们没有那些仅对两个参数求和并返回结果的函数,我们在许多示例中都可以看到它。 好吧,我遇到了一个过程,这对我来说真的很容易。 我要说的第一件事是,您无需更改体系结构即可开始单元测试。 当某些开发人员听到某种结构可以使代码更具可测试性时,这是一个错误。 然后他们等到转移到这种新的编码方式开始编写测试。 让我们不要那样做! 如果要测试现有代码,则可能需要重构某些部分,但绝对不要更改您的体系结构! 消息是对象在面向对象的世界中彼此交谈的方式。 您希望对象执行方法时将消息传递给该对象。 获取属性值是相同的,并且适用于其他所有条件! 知道要测试什么的过程包含三个简单的步骤 ,并且都涉及消息。 1.确定消息的类型 消息有两种可能的类型:查询和命令。 这可能很明显,但是查询是当您要求某事时,而命令是当您告诉它要某事时。 查询具有返回值,但是在命令没有返回值时却不会更改任何状态,但是会产生一些副作用。 查询 =返回什么,什么也没改变 命令 =不返回任何内容并更改某些内容 2.标识消息的来源 一条消息可能有三个来源:传入,传出和发送给自己。 传入 :当一个对象从外部(另一个对象)接收到消息时。 传出 :当对象向外部发送消息时。 自我发送 :猜猜是什么?! 当对象向自身发送消息时。 3.遵循以下图表: 哦! 多么棒的图表! 对?! 保持这种状态直到对您自然。 第一个例子是采用Equatable。 该函数具有返回值,并且不会更改任何状态。 这是一个查询! 它是公共的,可以由另一个想知道两个Wallet是否相等的对象调用。 进来! 传入查询:声明结果。 传入查询非常简单。 您有一个预期的结果,然后调用该函数并断言它(如果它返回了预期的结果)。 (单词sut代表sut系统。在下面的示例中也将使用它) 第二个例子:在loadView之后,您想确保您的插座正确钩住! 他们在这一点上不应该零。 该函数没有返回值,调用后应设置出口。 因此,它有副作用。 所以,这是一个命令! (副作用是直接公开的,因为出口是sut上的财产 ) 该模块是公共的,可以从外部调用。 […]

使用TestScheduler测试反应式代码

TL; DR 异步反应性函数,例如Observable.interval() , Observable.timer()或您自己的异步反应性函数,可以通过沿时间轴移动被测对象来使用RxTest.TestScheduler进行测试。 真正忙碌的人可能会立即深入那里的代码: vadimue / RxLocation RxLocation –反应性使用CoreLocation github.com 简单样本 我们都写测试,不是吗? 😏有时我们甚至使用TDD。 通常,我们将Observables用于多个异步操作。 但是用单元测试来测试它们可能很棘手。 如果我们不编写DispatchQueue.main.asyncAfter()或wait(for: [expectation], timeout: 60)异步测试将失败。 否则,我们可能将Rx用于同步操作,这就是为什么我们不会在执行等待时遇到问题。 例如,让我们看一个测试,该测试涵盖了当用户点击单元格时打开带有详细说明的页面。 该测试同步运行,这就是为什么它呈绿色的原因。 与时间有关的要求 有时需要描述测试中与时间相关的要求。 如果您需要测试发送API请求并同时显示UIActivityIndi​​cator怎么办? 还是基于某些延迟下载数据? 测试可能需要一段时间才能运行,因为它们必须等待操作完成。 显然,我们旨在加快运行测试的速度,因为我们经常运行测试并且不想浪费时间。 那么我们应该如何为这些情况编写测试? 我建议从业务需求入手。 假设您创建了一个用于监视用户位置的应用程序: 您可以使用CoreLocation框架; 实现CLLocationManagerDelegate并使之具有响应性(重用来自官方存储库CLLocationManager + Rx.swift和RxCLLocationManagerDelegateProxy.swift的扩展); 使用这些扩展创建单独的服务; 之后获取微小的LocationServiceProtocol的实现。 方法location()返回原始CLLocation对象序列。 在当前的实现中,它们每秒以最简单的配置到达。 那么,需求呢? 位置数据传输应该是周期性的,取决于运行和以下规则: 传输必须至少每分钟执行一次(静止),并且位置变化超过10 m; 传输的频率不应超过每10秒一次。 为了使代码简单(嗨,SRP!),我创建了单独的类,该类将过滤虚拟位置序列。 TrackingService符合该协议: 让我们从描述第一个要求开始: 传输必须每分钟至少执行一次。 要遵循TDD,我们应该首先编写测试。 为此创建并设置XCTestCase类: 时间旅行 现在我们可以编写我们的第一个测试。 这是最终版本。 […]

单元测试—具有Nimble + Snapshot的UIView

大家好,我目前正在撰写一系列文章,展示如何为iOS编写某种测试,这是第一篇。 在上一篇文章中,我谈到了TDD和一些测试框架,这些框架在讨论iOS平台上的测试时对我有很大帮助。 在这里,我们将看到如何对XIB文件创建的UIView进行单元测试,或使用制图通过代码对UIView进行单元测试。 如果您想了解有关制图的更多信息并查看代码,可以在这篇文章中查看。 现在让我们了解测试! 设置您的项目 首先,我们需要在项目上安装Quick,Nimble和Nimble + Snapshot。 您可以通过将Podfile pod ‘Nimble-Snapshots’和Podfile pod ‘Nimble-Snapshots’到Podfile来Podfile 。 不要忘记在测试目标上设置这些框架,如果没有框架,则可以在项目中创建一个新框架。 为此,您可以选择项目->目标-> +-> iOS单元测试包,并为其命名。 您的Podfile应该如下所示: 我们的swift文件只是一个简单的UIView自定义类: 使用查看代码 现在,让我们使用视图代码创建相同的测试。 首先,创建新的UIView自定义类文件,添加两个UITextField和一个UIButton并使用制图添加约束。 就像我们之前使用XIB进行的测试一样,这些屏幕截图将在您的项目文件夹中创建。 您可以随时检查它们。 Nimble + Snapshot如何工作? 您可能会问我-我怎么知道我何时打破观点或我打破了什么? Nimble + Snapshot会创建差异屏幕截图,而在您破坏测试时会失败。 让我们以实际的方式来看一下。 让我们注释一下我们的textFieldDidChange()方法以删除隐藏/显示行为。 如果您查看日志(可以按png过滤),Nimble + Snapshot会打印失败测试图像的路径。 打开Finder->转到->转到文件夹(Cmd + Shif + G)并粘贴路径。 路径类似于:/ /Users/youruser/Library/Developer/CoreSimulator/Devices/adeviceid/data/Containers/Application/anapplicationid/tmp/LoginViewTests/failed_LoginView_should_show_loginButton_if_user_set_username_and_password@2x.png 该文件夹包含三个图像:引用,失败和差异。 根据这些图像,您可以查看代码中的错误并进行修复。 测试UIView是了解您在编码时是否破坏某些东西或代码是否按预期运行的好方法。 如您所见,我们不需要编写测试来验证显示/隐藏行为,我们通过快照视图进行检查。 如果您想了解有关Snapshot和UITesting的更多信息,可以在realm.io上查看此演讲。

TDD是“落后”

在向同事教授TDD时,经常会出现相同的问题: 我们从哪里开始开发软件? 这个问题导致了很多讨论和理解TDD的艰辛。 我看到了从哪里开始的不同方法,但是大多数情况下,人们倾向于使用用户界面。 这样做的原因是能够执行交互并查看他们是否实现了正确的行为。 在TDD中执行此操作非常繁琐。 您从一个按钮开始,然后检查它是否为nil。 好的,是这样……按下按钮时,我想查看用户搜索的电影列表。 嗯,我还没有测试,因为我还没有任何数据! 该怎么办? 一种选择是传入伪数据,并在每次添加一些代码时将其逐段替换为实际数据。 这似乎很好,直到您意识到需要大量工作并且开发速度缓慢。 另一种选择是不以用户界面开头。 而是从网络请求开始。 从这里开始,您的测试将确切知道预期的内容并正在测试实时数据,而不是模拟(当然,下一步是对请求进行存根,但我们还没有完成)。 编写完请求后,我们可以添加解析,然后可以添加存储等。通过这一切,我们知道我们拥有的工作代码不基于假设。 自下而上的一个问题是股东。 他们希望看到您软件中的更改,而他们看不到创建网络堆栈。 不要陷入模拟所有数据并仅创建用户界面以使股东满意的陷阱。 在某个时候,您将不得不编写其余的内容,然后他们将抱怨更多,因为开发速度很快,现在没有新内容了。 除了默认的TDD做法,这没什么别的主意: 从您所需的最小代码开始,并为此编写测试。 结论 让这个主意变得不那么容易。 它要求纪律首先编写测试,而不添加不必要的代码。 仍然遵循这一纪律并坚持下去将会产生巨大的成果,上面所写的想法只会帮助您入门。 实践TDD本身将得出这个结论。 如果不是这样,我很想听听! Next:Swift中的高级模拟 上一篇:联网怎么样?

单元测试中的存根网络调用

确保您知道您的回应 几年前,我被要求支持公司的其他分支机构。 他们的iOS开发人员很快就离开了,他正在开发的应用程序即将完成。 我去那儿修复了大约10个错误……它以50多个问题结束,但是我们都知道,这在开发过程中很正常。 尤其是当您有一位对利润非常挑剔的出色设计师时。 无论如何..我到了那里,想测试一些网络代码。 我以为我既年轻又年轻(大约一年前才开始使用Kiwi),所以我可以使用这些方法,然后继续前进。 我的一个同事不太喜欢这个主意。 她建议使用当前存在的依赖项,而不要引入新的依赖项(在此期间,我们使用的是XCTest,而不是Kiwi)。 那么她的解决方案是什么? 她已经在使用KIF,并且针对后端运行UI测试并不好玩。 而是使用OHHTTPStubs。 使用此库,您可以拦截网络请求并返回所需的任何内容。 我的猜测是,它在NSURL上使用方法刷新进行拦截,但是如果有库,为什么要自己做。 如何使用 编写拦截器非常容易。 只需指定要拦截的主机和路径并返回响应即可。 我倾向于在包含我的拦截器的OHHTTPStubs上使用类方法创建扩展。 响应是您的JSON对象。 根据要返回的内容,您可以使用库提供的初始化程序。 我还创建了一个匹配器,以便于检查是否调用了正确的网址。 OHHTTPStubs提供了一些,但是有时您必须编写自己的。 这是通过创建OHHTTPStubsTestBlock来完成的。 这是一个闭包,将在其中传递一个RequestObject并返回一个Bool。 与往常一样,要设置我们的套件,我们可以使用beforeEach或beforeSuite。 只是不要忘记事后清理! 所以请不要忘记: OHHTTPStubs.removeAllStubs() 结论 我喜欢这个图书馆。 这是您进行单元测试所需的全部。 几个月前,我向团队介绍了它,从那时起,我们的网络测试就针对OHHTTPStubs进行了。 使用OHHTTPStubs使我们能够测试整个应用程序,而不必在生产系统中引入任何测试代码。 这样,我们可以确定,只要后端api不变,我们的应用程序就会按照我们期望的方式运行。 下一步:TDD向后 上一篇:测试苹果MVC

在Swift中完善自定义断言

单元测试很棒,不是吗? 它不仅可以帮助您改进代码,而且可以确保下次更改它时不会弄乱任何东西。 测试的不利之处在于编写测试很容易,最终导致阅读困难! 在这里,我将介绍一些有用的技巧,这些技巧可以使您更容易阅读和理解单元测试代码。 断言及其使用 如果您之前没有编写过测试,则可能没有看到典型的测试断言。 断言(就像软件开发中的许多事物一样!)具有多种形状和大小,但是大多数情况下,它们是一段检查(或断言 )某些东西的代码。 if语句实际上是一个断言,在下面的示例中,我们断言该数组不为空: 如果imagesArray.isEmpty == false { //在这里做点事 } 在测试中,断言是单元测试的关键点-我们使用它们来断言 (或检查)我们的逻辑是否为我们提供了预期的结果: func testTwoMultipliedByTwoIsFour(){ 让计算器= Calculator() 让结果= Calculator.multiply(2,by:2) XCTAssertEqual(结果,4) } 在上述情况下,行XCTAssertEqual(result, 4)是断言—我们断言结果是4。 您可以在这里找到每个XCTAssert方法的完整描述。 在下面的演示中,我们可以看到样本测试失败时的外观。 您可以使用XCTFail()始终触发失败-在这种情况下,如果我们有一个if let可能无法通过,这很有用。 重要的一点是Xcode中的测试默认通过 ,因此我们必须记住在计为失败的分支上显式失败。 但是,这里的断言虽然很详细,但占用的行数却是其余测试的两倍! 让我们提取一个自定义断言以使其更具可读性。 定制断言 我们可以编写自己的自定义断言,这在断言可能变得复杂或最终导致在测试之间复制断言并实践三击规则的情况下非常有用。 我们可以提取断言,如下所示。 这使断言在测试方法本身中更小,并且还使我们对实际检查的内容有了更好的了解,而不必费力检查两个if let语句。 我们的断言现在可以正常工作。 但奇怪的是,断言失败不再显示在测试中,而是显示在我们的辅助方法中。 在运行测试时,这很烦人,因为您必须多花一些时间才能发现错误所在。 当多个测试使用相同的断言时,这确实很烦人,因为它们都在同一行上使断言失败! 我们想在调用自定义断言的地方显示失败,但是当前在我们的自定义断言的XCT函数显示失败。 幸运的是,我们可以改变这一点。 有些人会更喜欢在其自定义断言函数前加上XCT前缀,以使其看起来像内置断言方法,例如XCTAssertEquals(_,_) 。 通常不建议使用其他框架的前缀,但可以帮助您更快地在自动完成结果中找到自定义断言。 寻找最适合您的团队的东西。 更改显示断言的位置 我们有一种方法可以指出断言失败,使其看起来像来自调用自定义断言方法的位置,而不是来自其内部。 每个XCT函数都带有两个方便的默认参数-用于指示将红叉放在Xcode中的位置以及在哪里显示错误消息。 它们是file和line ,并且有两个对应的特殊“宏” […]

为什么我要编写快照测试?

测试是任何高质量软件的组成部分。 我相信每个公司或软件发行商都必须先验证产品的质量,然后才能投放市场。 据说,如果质量好,它将吸引更多的用户。 这个事实也适用于软件开发。 我们以手动或自动方式执行UI测试,以确认应用程序的行为正确并具有符合规范的所有功能。 如果我们进行更细化的单元测试,则可以测试单个单元。 我们通过编写肯定的测试(即正确通过测试)和否定的测试(即测试意外数据到来时单元的行为)来涵盖所有极端情况。 编写自动化测试以捕获UI“正确性”实际上是不可能的。 测试UI困难的原因是,UI最小模块的显着细节很难以编程方式表达。 正确性不能由输出的文本部分确定。 我们希望质量保证将重点放在需要人工关注或可以通过自动化完美测试的功能的确切状态下的确切组件上。 但是UI单元测试呢? 好吧,我们为此提供了一个解决方案: 快照测试! iOSSnapshotTestCase如何捕获快照? iOSSnapshotTestCase提供FBSnapshotVerifyView(view)方法来将期望与参考快照进行比较。 如果不存在参考快照,它将捕获。 这个方法使用`renderInContext()`来捕获快照,但是`renderInContext()`有一些限制,您不能使用它来测试Visual元素或UIAppearance。 为了测试这些元素,还有另一种方法“ drawViewHierarchyInRect”。 要使用此方法,您需要在测试中设置`useDrawViewHierarchyInRect = true`。 drawViewHierarchyInRect的性能不好,所以我建议仅在确实需要时使用useDrawViewHierarchyInRect = true。 iOSSnapshotTestCase如何比较快照? 有很多比较图像的方法。 我在演讲中得到的第一个答案是逐像素比较图像,这是正确的,但在逐像素比较之前,我们还需要考虑其他方面。 让我们看看它如何进行比较: 首先,它比较图像大小。 如果不匹配,则测试失败。 如果容差率为零,则使用C函数“ Memcmp()”在内存级别比较图像。 为此,它使用calloc()在内存中分配空间,并使用CGContextRef在内存中绘制图像。 如果容差率> 0,即表示您可以接受,即使存在x%的差异,它也会通过对numberOfPixels(即图像宽度*高度)进行迭代来逐像素比较图像。 iOSSnapshotTestCase的功能: 它会根据测试类和选择器自动命名磁盘上的参考映像。 如果要在单个测试方法中执行多个快照,则需要提供一个可选的“标识符”。 使用`isDeviceAgnostic`。 如果您在测试中将此属性设置为true,它将在测试名称中附加设备型号,操作系统编号和大小。 优点: 快照测试易于编写。 在执行重构时,您不必担心,就好像只有代码重构一样,测试也不会失败。 解耦:视图将松散耦合。 易于验证不同尺寸的布局。 我建议以一个模拟器(即iPhone 6)为基础来捕获快照。 如果您使用3倍快照的iPhone 8,则尺寸会很大。 如果您使用的是CI / CD工具,请确保也在那里设置了默认模拟器iPhone 6。 […]

通过创造混乱来进行单元测试竞赛条件(快速)

多线程竞争条件不会始终如一地发生,这使得它们很难重现。 另一方面,最佳软件实践要求我们编写自动化测试以验证我们的更改。 我将分享我的技术来创建足够的混乱,以使那些罕见的崩溃变得足够可靠以进行测试。 什么是比赛条件? 如果您有多个线程试图同时变异和访问数据,可能会发生不好的事情。 一个线程可能会更改一个值,而另一个线程正在使用它! 避免这种情况的最好方法是以不可能的方式构造代码。 Swift的结构是一个很好的起点,因为它们是在写操作时复制的(额外的功劳:从功能上考虑!)。 但是我们不能总是做理想的事情。 我们在现实世界中发展,但我们的控制范围之外。 也许我们陷入了遗留代码库。 也许我们没有时间或金钱来重构所有东西。 有时,我们所能做的就是实施最佳实践。 最佳实践之一是: 修复错误时,编写一个可重现该错误的单元测试以防止退化。 通常这是直截了当的,但是这些竞争条件崩溃可能很棘手。 它们很少出现在崩溃报告器中,并且您从未在设备上看到过这种情况。 让我们崩溃 首先,设置一个包含一些不安全代码的简单方法: 不料? 更像预期的那样。 现在,您可以修复它,并且可以放心,将来的更改不会使它恢复正常。 肖恩每天在 Livefront对抗 混乱 。

Metal Camera教程奖金:在iOS Simulator中运行Metal项目

在《金属相机教程》系列中,我们创建了一个简单的应用程序,可以在屏幕上实时渲染相机帧。 但是,此应用程序使用的是Metal框架,iOS模拟器中没有该框架。 基本上,如果您选择模拟器作为构建设备,则您的应用甚至都无法构建,这很可惜,例如,要添加单元测试,而无需在未将实际设备连接到计算机的情况下就可以运行它们。 Metal某些部分具有针对桌面处理器体系结构的存根实现,这意味着您至少可以构建该应用程序(仅是发现它在模拟器上无法正常工作)。 Simulator甚至不存在其他部件,例如MetalKit ,因此您必须将导入包装到条件编译块中,如下所示: import UIKit import Metal #if arch(i386) || arch(x86_64) #else import MetalKit #endif 我们检查当前的处理器体系结构是否为台式机,在这种情况下根本不导入任何仅ARM的框架。 请注意, MetalKit中的类也不可用,因此您也需要使用它们包装任何代码: public class MTKViewController: UIViewController { #if arch(i386) || arch(x86_64) #else /// `UIViewController`’s view private var metalView: MTKView! #endif // MARK: – Public overrides override public func loadView() { super.loadView() #if arch(i386) || arch(x86_64) NSLog(“Failed […]

为什么要编写单元测试?

作为软件工程师,我们总是编写代码,但是测试代码又如何呢? 当有人要求您编写单元测试时,还有更多的执行程序。 1,我不知道怎么写单元测试 2,写作单元测试是如此艰难 3,我的代码太简单了,无法测试 4,我没有足够的时间编写单元测试。 ………………………………。 不 没有人喜欢写它。 如果您想编写测试代码来确保您的代码,以减少奇怪的错误。 首先,您必须选择出色的建筑。 MVC怎么样? 1,大量的viewController 除非您是初级ios开发人员,否则请放弃,否则您将永远不会这么做。 众所周知,MVC意味着Massive ViewController,它承担着更多的责任,因此它违反了单一原则,您可以让它承担更多的任务。作为高级开发人员,我们必须编写符合SOLID RULES的代码-确保您的代码具有高品质,如果您总是写不好的代码,如何提高自己的技能。 这里我没有提到编写干净代码的主题,但是,如果您学习它,可以阅读本书的名字Clean coder,作者将他的最佳实践贡献给我们,谢谢他。 清洁编码员:培训视频。 具有个性。 对于软件专业人员。 Clean Coders是为软件专业人士提供指导性视频的领先制作人,其教学方式可同时兼顾… 2,艰难测试 因此,我们开始从Microsoft公司+ RxSwift + Coordinator中选择另一个著名的MVVM。 还有更多博客要写,所以我不提它…… 众所周知,还有更多的博客谈论架构,但这并不完美。重要的是您要在项目中使用哪个角色…… 几天来,我写了一篇有关清洁架构的博客,我认为架构的重点是您必须对其进行设计……设计—使您的业务清晰明了…… 为什么要编写单元测试? 1,测试减少新功能中的错误 我们提倡在编写新代码时编写测试。 测试不能消除错误,但是随着您添加新功能,它们会大大减少错误的数量。 2,测试减少现有功能中的错误 使用经过良好测试的代码,引入新功能很少会破坏现有功能。 如果一项新功能破坏了现有功能,则现有测试将立即失败,从而使您可以找出问题并加以解决。 没有测试,您可能会引入几天或几周内找不到的错误。 3,测试是一个很好的文档 简洁的代码示例胜于文档的许多段落。如果阅读文档,我们总是在不同文件之间来回移动。 4,测试是如此有趣 如果您想挑战自己,写作测试将非常有趣…… 5,测试改善你的设计 编写测试会迫使您使代码可测试。 具体来说,您倾向于较少依赖诸如单例和全局变量之类的可疑模式,而是使类松散耦合且更易于使用。 紧密耦合的代码或需要复杂初始化的代码很难测试。 6,测试使开发更快 众所周知,测试可以减少错误,我们将大部分时间花在调试应用程序上。10%的时间编写代码,当您的代码变得越来越多时,错误很难调试,因此这会减慢您的开发速度。 7,测试可以迫使自己放慢脚步思考 开发人员总是先犯一个常犯的错误,然后立即开始编写代码……愚蠢的行为要做,这会使您感到痛苦。 8,测试减少恐惧。 如果您从未编写过Test,如何知道您的应用程序运行良好。所以让您远离恐惧,今天就开始编写Unit test。 […]