Tag: 单元测试

iOS中的单元测试

第3部分:快速灵活的测试 前言 我打算编写完整的iOS单元测试系列。 您可以在下面的链接中找到成品。 快乐阅读🙂 第1部分:为什么在开发应用程序时需要进行单元测试? 第2部分:我的第一个单元测试 第3部分:快速/灵活的干净测试 第4部分:如何处理上下文 第5部分:良好做法 第6部分:单元测试,Jenkins和SonarQube一起[待撰写] 借助XCTest框架,您已经可以为应用程序编写所有可能的单元测试。 但是,一旦您做了很多测试用例,并且在测试类中有多个上下文,您就会发现使用XCTest并不是那么酷。 您可能会对使用Quick&Nimble感兴趣。 这两个框架(主要是Quick)将帮助您组织测试类中的测试,以使其更清晰,更易于阅读并且更容易在一个测试类中具有多个测试上下文。 使用Quick,您将为编写异步功能测试感到更加自在。 将Quick&Nimble集成到项目中的最简单方法是使用可可豆荚或依赖管理,例如Carthage甚至Swift Package Manager(Quick&Nimble支持这3种方法)。 如果与CocoaPods一起使用,请在项目根文件夹的终端中运行pod init ,以为项目初始化Cocoa Pods。 然后使用任何文本编辑器添加Quick&Nimble,如下所示: 最后,运行pod install或pod update安装那些框架。 完成后,打开项目的.xcworkspace而不是.xcodeproject 。 考虑让一个Translator类从给定的键中获取本地化的字符串。 该翻译器能够处理多种语言,并以所选语言返回正确的文本。 如果我们使用XCTest为该类编写单元测试,我们将具有以下内容: 导入XCTestclass转换器XCTests:XCTestCase { 覆盖func setUp(){ //当我们有1个测试上下文时,此函数很有用,或者我们可以使用它来初始化公共上下文 } func testSetupTranslator(){ 让翻译器= Translator() 让yesButton = translator.message(“ yes_button”) XCTAssertNotNil(yesButton,“应该能够获得有效的本地化字符串”) XCTAssertEqual(yesButton,“是”,“应该使用英语作为默认语言”) } func testTranslationInFrench(){ 让翻译器= Translator() translator.setUp(“ fr”) […]

Swift Con XCTest的密码

Una de lasprinciples dudas a la hora de querer实现者单元测试使用Swift,no saberqué框架,como configurarlo和cuales pruebas escribir。 您可以使用XCTest的框架或其他工具 。 Funciona muy bien,es muyfácilde configurar和sobre todo muyfácilde usar。 可轻松实现XCTest的集成Xcode,并以简单的方式实现Xcode的缺陷。 Tenemos dos opciones a la hora de Implementarlo,在没有新的合作伙伴的情况下,在新的和新的合作伙伴之间进行合作。 不动产公司 通用X编码和新的通用编码,可以使用cascas函件和清单进行单向激活。 普遍存在的不动产 巴拉圭人参政党和巴拉圭人民党的存在。 Solamente necesitamos agregar una nueva unidad de pruebas unitarias。 特纳莫斯的香蒜酱 Hay que dar da clic albotónde agregar en la […]

使用构建器模式进行下一级Swift单元测试

介绍 单元测试对于软件项目至关重要,但是会增加开销。 在本文中,我将讨论如何利用构建器模式来提高团队编写,审查和维护单元测试的效率。 我将从谈论什么是模式以及为什么它在单元测试中特别有用开始。 然后,我将说明您可以通过使用该模式获得的收益。 最后,我将向您展示如何在Xcode项目中创建构建器类。 本文使用了使用Swift的iOS代码示例,但它们同样适用于几乎所有语言和平台。 可以在https://github.com/gearnshaw/BuilderPatternExample中找到本文中使用的完整示例代码。 什么是构建器模式? 构建器模式用于创建对象,将其设置与创建分开。 通过引入这种分隔,您可以:为对象中的字段设置默认值;为对象设置默认值。 仅覆盖需要显式设置的值; 提供复杂对象状态的预设; 并且,轻松创建复杂的对象图。 该模式在单元测试中特别有用,因为它们需要您一遍又一遍地创建对象,并为每个测试用例将它们设置为正确的状态。 如果您每次手动创建对象,您都会发现自己在重复代码,将对象设置从一个测试复制并粘贴到另一个测试,并在单元测试中编写各种混合的createMyObject()函数。 这些问题造成了重构的噩梦,使您难以理解测试的内容和原因。 构建器模式避免了这种情况,使您可以快速,干净且可维护地创建对象。 在测试中使用构建器模式的主要好处是: 意图交流 :从您的代码中可以明显看出测试中需要什么状态 易于重构 :当对象接口更改时,重构测试很简单 最少的代码 :测试代码简洁,样板代码少 这些共同导致了一个易于编码,审查和维护的项目。 考虑一个称为Device的对象。 它是一个简单的结构 ,但相同的原理适用于任何类型的对象,包括使用核心数据的对象。 我将在本文的示例中使用此对象。 建造者的解剖 这是完整的DeviceBuilder类。

测试序列的两种方法

未经请求的代码审查。 最近,我正在阅读使用Swift 3进行测试驱动的iOS开发,并且有一章他们测试从一个UIViewController迁移到另一个。 这让我思考了执行segue / present UIViewController的各种方法以及每种方法的简便性。 我想谈两种方式。 @IBAction将UIControl与@IBAction一起用于UIViewController并在代码中显示UIViewController -这是本书所建议的 将UIControl与UIStoryboard segue一起使用 当然,我认为#2(我的方式)更好,但我希望您能读懂并选择适合您和您的项目的方式。 该书建议将UIViewController嵌入UINavigationController ,其中UIBarButtonItem具有IBAction ,该IBAction呈现从UIStoryboard实例化的不同UIViewController 。 TLDR; 这是一张照片: 测试如下所示: 在我的设置中,我有一个UIStoryboard segue,它带有一个链接两个UIViewController的当前模态选项。 测试如下所示: 确实,没有代码。 这是情节@IBAction文件中的一次单击(基本上),而不是@IBAction ,您可以忘记将其链接到可能错误实现的代码。 好: 更少的代码 清洁代码 更清晰的测试命名 UIViewController和UIStoryboard之间的耦合UIStoryboard 测试标识符。 序列不需要标识符即可工作。 也就是说,如果有一个,稍后将更容易测试两个UIViewController之间的关系,而无需涉及UIBarButtonItem 我对此不满意: 我不喜欢在测试中使用value(forKey:) 。 在这里似乎很有必要,但仍然有种黑涩的代码味道 我认为测试目标是否具有标识符可能更清洁,并且当传递该标识符时, performSegue(withIdentifier:sender:)呈现正确的UIViewController 。 整个performSelector rigamarole似乎有点稠密 我希望这能给您一些启发,帮助您实现对UIStoryboardSegue进行单元测试的方式, UIStoryboardSegue您更多地考虑UIControl 。 我还希望您注意到这两个解决方案最令我困扰的是,没有人测试过以模态形式呈现UIViewController情况。 现在,您可以将UIStoryboardSegue从“ Show (eg Push)更改为“ UIStoryboardSegue Present Modally而无需中断测试。 作为设置的一部分,可以通过将UIViewController添加到UINavigationController来纠正这种疏忽,但是我认为添加此范围不在本文的讨论范围之内。 […]

单元测试— KIF和FBSnapshot

自从我谈论单元测试以来已经有一段时间了,在我们的应用程序和测试框架(如Quick和Nimble)中编写它们的重要性。 测试可以帮助我们保持功能正常运行,并避免新功能破坏旧功能。 我今天在这里谈论一个新框架:KIF。 这篇文章对KIF,优点,缺点,何时使用它以及一个示例进行了思考。 是一个单元测试框架,可帮助开发人员创建UI测试(是的,UI测试)并测试您的应用程序流,例如整个登录过程,例如,如果用户触摸键盘时键盘消失,则输入有效凭据,轻扫tableView单元格,当无效用户名的警报弹出。 它是用objc编写的,使用XCTestCase,但是如果您想将它与Quick一起使用,则可以使用KIF-Quick。 它带来了有助于执行事件的功能,如tap() , swipeLeft()和swipeLeft() 。 使用accessibility来标识要与之交互的对象,例如,您可以使用其accessibilityIdentifier或accessibilityLabel检索对象,然后使用tap()执行操作 viewTester().usingIdentifier(“btn_login”).tap() 您应该问自己-如果Apple提供了一个不错的UI测试框架,为什么我应该使用KIF-答案很简单:模拟! 通过KIF,您可以注入模拟对象来创建应用程序的强大本地测试流,换句话说,您可以自由模拟服务响应并测试整个应用程序流,而无需Internet连接,API响应甚至数据量。 哇! 那很棒。 那么,为什么我应该编写单元测试(如果KIF可以处理更多)? 这是个好问题! 的确,KIF可以处理单元测试以及更多内容,但是它需要在模拟器上运行并且需要时间。 因此,如果您决定仅使用KIF编写测试,则运行这些代码所花费的时间将比单项单元测试多得多。 如何使用它 通过cocoapods,KIF的安装过程很容易,只需将pod KIF添加到您的podfile运行pod install并完成。 KIF是用Obj-C编写的,因此您需要创建一个桥接头并向其中添加KIF。 #import 在此示例中,我们将测试带有成功和失败案例的简单登录流程。 将KIF和Snapshot的功能结合在一起(如果您不知道什么是Snapshot,则应该阅读此内容),我们将验证所有UI。 如果用户输入正确的值(在这种情况下,用户名是“ user”,密码是“ 123123”),则将显示主屏幕,否则将显示警告错误。 不要忘记测试目标并在将要交互的每个UI对象中设置可访问性标签。 DefaultFlowSpec测试 在此文件中,我们创建一个窗口,从情节viewController检索初始viewController并将其添加为窗口的rootViewController 。 之后,我们需要测试我们的成功和失败流程。 成功流程 我们的成功流程基于正确的投入。 首先,用户将在用户123123中设置user , 123123在密码字段中设置123123 ,我们现在要检查UI,然后单击login按钮并检查是否显示了homeScreen 。 成功流程 失败流程 在失败流程中,我们将按login按钮,检查是否显示错误警报,对警报按ok ,然后检查警报是否消失。 失败流程 结论 最好的方法是将单元测试和KIF结合起来以获得快速而完整的测试。 继续使用单元测试来测试您的方法和功能,这会更快。 使用KIF测试UI和MVC架构中模型,视图和控制器等层之间的连接。 我写了一些文章,展示了如何实现一些单元测试。 第1部分-具有Nimble + […]

Swift中的单元测试

作者:Matt North —高级IOS工程师 let greeting = “Hello!” 在Grindr,测试是我们开发工作流程的关键部分。 像许多公司一样,我们正在将iOS代码库过渡到Swift。 这是从几个方面进行的调整,Swift中的单元测试提出了特殊的挑战。 但是我们坚信单元测试所带来的价值。 那么,我们如何在Grindr编写Swift测试? 最佳实务 面对现实吧,对任何人来说,开始单元测试都可能是一项艰巨的任务。 对于Swift开发人员而言,更糟糕的是,Apple几乎没有文档说明如何编写可测试的代码。 无论您是重构现有的代码库以使其可测试,还是要着眼于可测试性来开始一个新项目,编写可测试代码都需要一个好的计划。 我将介绍2种技术/原理,这些技术/原理将有助于使您的代码更具可测试性: 依赖注入/协议驱动开发 函数式编程技术 依赖注入 依赖注入不是一个新主意,但在iOS社区中并不普遍。 依赖注入是在创建类时提供类的依赖(通常在初始化器中)的一种做法。 这样做可以使您的类可重用和自定义。 除了可重用代码的许多优点外,在编写单元测试时,这一点尤其重要。 让我们看一个例子: 错误的方法 class UserRepository { static let shared = UserRepository() private let dbContext = NSManagedObjectContext() // returns an array of users func getUsers() -> [User] { let fetchRequest = NSFetchRequest(entityName: “User”) […]

如何在Swift中测试抛出代码

您必须接管多少次进行了单元测试的项目,但是这些项目很难掌握,拼命失败,或者甚至无法建立测试目标? 保持单元测试代码的健壮性和可维护性,而不是让它们随着时间的流逝而被遗弃和忽略,至关重要。 在Storytel,我们尝试使单元测试简短易读。 由于测试代码的性质,代码往往会增长很长且具有重复性,因此保持代码整洁很重要,然后随着项目的增长,进行测试的工作就不会变得太乏味。 有时,抛出的代码可能很难测试,并且最终的测试代码可能很难看。 在本文中,我将深入探讨不同的场景,以及如何以出色而强大的方式解决它们。 XCTest是一个功能强大的框架。 在Xcode 8.3中,Apple引入了几个新的XCTAssert函数,除了几个现有的函数。 尽管它们提供的功能允许执行开发人员希望执行的大多数操作,但某些操作仍然需要在本机提供的功能之上的样板代码。 让我们看看一些案例以及我们如何解决它们。 比较抛出函数的结果 这很容易。 现有的所有XCTAssert函数都已经带有抛出参数。 如果结果为Equatable ,则将执行以下操作: XCTAssertEqual(尝试x.calculateValue(),ExpectedValue) 如果calculateValue()引发错误,则测试失败,并显示消息“ XCTAssertEqual失败:引发错误…… ”。 如果调用未引发并且两个值不相等,则测试失败并显示消息“ XCTAssertEqual failed:a不等于b ”,其中“ a ”和“ b ”分别是由String(describing:) 。 简而言之,所有XCTAssert *值检查函数都会验证调用不会引发任何错误 并返回预期结果。 很方便。 具有非等于返回类型的函数 通常XCTAssertEqual及其值检查同级是不够的。 如果函数不返回值,或者在测试用例的上下文中可以将其忽略,则可以使用Xcode 8.3中可用的新XCTAssertNoThrow函数: XCTAssertNoThrow(尝试x.doSomething()) 有趣的是,在Xcode 8.3出现之前,我们有一个具有完全相同签名的自定义函数,除了删除自定义实现之外,我们无需更改任何代码。 另一种常见情况是,当返回的类型不是Equatable ,或者如果我们只想检查返回结果的某些属性。 即使类型是 当对象的描述很长时,相等的,相等的测试用例失败几乎是没有用的:很难确定哪个字段的值不正确: 在我们的项目中,我们有很多函数会产生不符合Equatable复杂类型。 一个常见的例子是数据模型对象-我们将它们公开为协议以隐藏内部实现,并且我们不希望它们是平等的。 每种模型类型都有一个带字典的引发初始化器。 在某个时候,我们意识到我们的单元测试看起来很可怕。 他们有重复的代码,没有意义的可选内容,并且当这些测试失败时,男孩们所有的东西都是红色的。 更糟糕的是,有很多复制粘贴。 代码如下: XCTAssertNoThrow(尝试BookObject(dictionary:sampleDictionary)) 让书=尝试? BookObject(字典:sampleDictionary) XCTAssertEqual(book?.name,“ […]

单元测试— UITableView

向用户显示数据的最常见方式是使用某种无休止的表格,例如Instagram的feed,Facebook的时间轴,WhatsApp的消息等。 作为iOS开发人员,您有一些工具可以实现这一目标。 您可以使用UITableView , UICollectionView , IGListKit等。 所有这些工具都有共同点,它们都使用委托/数据源模式,并具有用于显示数据的自定义视图。 在本文中,我将展示如何使用UITableView测试集合的委托,数据源和视图。 数据源 首先,让我们创建一个新文件并从UITableViewDataSource实现一些方法。 在此示例中,我们将使用默认的UITableViewCell作为单元格,使用String作为模型,但是可以随意使用所需的任何单元格和模型。 根据[String]?我们的单元格将具有不同的标题[String]? 数据。 MyDelegateDatasource文件 之后,让我们编写一些测试。 在每次测试之前,我们将创建一个[String]来填充我们的表,创建一个MyDelegateDatasource对象,将UITableViewCell注册到我们的表中并设置其委托和数据源。 目前,我们有两项测试: 行数 节数 我们的行数应为5,并应包含1个部分。 cellForRow方法将在以后进行测试。 MyDelegateDatasource测试文件 代表 我们的代表有点不同,要测试它,我们需要对其进行模拟,一种简单的方法是创建一个协议。 每次用户选择一个单元格时,我们都将其称为该协议。 因此,现在我们可以更新MyDelegateDatasource以具有MyDelegateDatasourceProtocol对象,并在每次UITableViewDelegate调用didSelectRow时都调用它。 不要忘记实现UITableViewDelegate didSelectRow方法。 MyDelegateDatasource文件 为了测试该委托,我们需要创建一个实现该协议的对象,换句话说,我们需要对其进行模拟。 我们将使用此对象检查在UITableViewDelegate调用didSelectRow时是否正在调用我们的委托。 这个模拟很简单,只需要在调用didSelectCell方法时更改一个布尔值并保存接收到的参数即可。 MyDelegateDatasourceMock文件 让我们更新测试文件,创建一个MyDelegateDatasourceMock对象并在MyDelegateDatasource上设置委托属性。 我们的委托测试是在调用tableView(tableView, didSelectRowAt: indexPath)之前检查didSelectCell属性是否为false之后是否将其更改为true 。 另外,我们正在验证收到的数据是否符合预期。 视图 我们的测试未涵盖cellForRow方法。 为了检查UITableViewCell是否按预期方式运行,我们使用了Nimble + Snapshot。 让我们添加一些测试来检查它们。 MyDelegateDatasourceTest文件 在我们的项目中,在自己的文件中实现委托和数据源将在测试时大有帮助,并使它们更易于编写和测试。 这是一系列文章的第4部分,展示了如何在iOS上测试某些内容。 第1部分-具有Nimble + Snapshots的UIViews 第2部分-单元测试-JSON的解析 第3部分-单元测试-网络请求 附:如果您喜欢这篇文章,请在Twitter上分享,或在中级推荐,或两者都=)。 […]

掌握XCTestExpectation的4个技巧

我相信,使我们成为优秀的程序员的一种愿望就是掌握和改进我们所有人拥有的工具集。 考虑到这一点,在华沙BlaBlaCar办公室的一个闪亮的早晨,我停了一会儿,看看如何才能从旧的XCTestExpectation更多XCTestExpectation 。 在本文中,我将向您展示一些现在可以用来更有效地编写单元测试的内容。 这4个技巧可用于测试异步代码。 我将用来自不同领域的实际例子来说明我的想法。 有时有必要测试给定的异步代码是否多次执行。 .expectedFulfillmentCount使它变得不那么容易。 它表示在完全满足期望之前必须调用满次fulfill()的次数。 示例:考虑一个可以通过调用execute()启动的Task对象。 它将状态更新报告给委托对象。 我们要测试的是,一旦任务开始,它的状态就异步地变为downloading , processing并最终finished : MockTaskStatusDelegate执行didCall_taskDidChangeStatus块以确认调用了适当的委托方法。 在此结束语(第21行)中, “代表被称为3次”得以实现。 我们使用recordedStatuses辅助数组来跟踪状态历史recordedStatuses ,并在期望完成后断言其正确性。 若要测试未执行给定的代码,可以使用.isInverted属性。 如果满足,“反向”期望将失败。 在测试互斥流或仅在给定的事情应该在一种配置下发​​生而不在另一种配置下发​​生时,它很有用。 示例:假设我们构建了一个游戏,特别是关卡选择屏幕。 某些级别可用,而其他级别则被锁定 。 轻按第一组中的一个按钮即可开始播放,轻按上一个按钮则无济于事-这是我们要测试的行为。 从技术上讲,该屏幕由LevelSelectionViewModel表示。 当用户点击按钮时,它公开从视图调用的selectLevel(atIndex:)方法。 游戏开始时,应使用didRequestOpeningLevel(withIdentifier:)通知didRequestOpeningLevel(withIdentifier:) : 与前面的示例相似,当调用委托方法时, MockLevelSelectionViewModelDelegate执行didCall_didRequestOpeningLevelWithIdentifier块。 视图模型配置有两个级别: .unlocked和.locked ,分别存储在索引0和1 。 第一个单元测试模拟在索引0上的敲击,并断言满足了期望。 第二个测试将期望值配置为反转(第38行),因此只有在给定的超时时间内未执行第41行时,期望才会通过。 值得一提的是,如果与第一个测试相辅相成,第二个测试将更有价值。 第一个测试确保视图模型与其委托人对话,而第二个测试则将此逻辑限制为仅.unlocked按钮。 只有后者会给我们一种错误的正确感。 例如,想象一下, LevelSelectionViewModel中存在一个错误,导致永远不会调用该委托。 第二项测试将通过(无法满足反向期望)。 没有第一个测试,我们可能会认为一切都很好,但事实并非如此。 在这样的示例中,第一个测试将失败,因为它的期望无法实现。 在测试方法的末尾不一定必须验证期望。 可以使用以下方便的XCTestCase函数来完成此操作: func wait(for: [XCTestExpectation], timeout: TimeInterval, enforceOrder: […]

关于setUp()和tearDown()的全部

Xcode运行测试时,它将独立调用每个测试方法。 因此,每种方法都必须准备并清理所有分配。 为此,它具有两个重要的方法setUp()和tearDown() 。 setUp() —在调用给定类中的每个测试方法之前,将调用此方法。 tearDown() —调用给定类中的每个测试方法后,将调用此方法。 每个类测试都从类 setUp() 方法执行开始。 然后针对每种测试方法 1.分配了一个新的类实例。 2.执行其实例setUp()方法。 3.运行测试。 4.执行实例tearDown()方法。 对于该类中的所有测试方法,都将重复此顺序。 在最后一个测试方法之后,当实例tearDown()已运行时,Xcode将执行类teardown方法。 您可以通过5种不同的方式自定义setUp()和teardown() , 1.覆盖setUp()类方法以设置所有测试方法的初始状态。 2.覆盖setUp()实例方法以在执行每个测试方法之前重置初始状态。 3.在执行测试方法期间,可以使用addTearDownBlock(_:)方法使用addTearDownBlock(_:)的拆卸代码块。 4.覆盖每个测试方法执行后要清除的tearDown()实例方法。 5.在执行所有测试方法后,重写tearDown()类方法以进行清理。 让我们检查以下测试的执行情况, //资源: developer.apple.comclass SetUpAndTearDownExampleTestCase:XCTestCase { 覆盖类func setUp(){// 1。 super.setUp() //这是setUp()类方法。 //在第一个测试方法开始之前被调用。 //在此处设置所有总体初始状态。 } 覆盖func setUp(){// 2。 super.setUp() //这是setUp()实例方法。 //在每个测试方法开始之前调用它。 //在此处设置任何每次测试状态。 } func testMethod1(){// 3。 //这是第一个测试方法。 //您的测试代码在这里。 addTeardownBlock {// 4。 //当testMethod1()结束时调用。 […]