Tag: 单元测试

具有60个单元测试的示例iOS应用

无需功能性反应式编程的MVVM架构模式 在互联网上,缺少使用单元测试的应用程序的实际示例。 有一些关于抽象理论中的单元测试原理的文章,但是很少有明确的具体示例。 因此,我决定制作一个应用程序,以分享到目前为止我学到的一些知识。 这是运行该应用程序的视频: 代码覆盖率尚未完成,但没有必要达到100%: Xcode单元测试会在每个单元测试之前自动运行setUp()方法,并在每个测试方法之后自动运行tearDown()方法。 这些方法通常用于清除内存中状态,以防止其从一个测试传播到另一个测试。 在setUp()中,我调用resetState方法来清除数据事件状态。 在ShowsRepository中,我仅在内存中使用Realm来在每次运行测试时都不会在磁盘上保持状态,这可以防止单元测试相互干扰。 让配置= Realm.Configuration(inMemoryIdentifier:“ InMemoryRealm”) 假物件 进行单元测试必不可少的是假对象。 您可以在此处阅读更多有关为什么虚假物品如此重要的信息(“一种科学实验”)。 在应用程序中,我使用了虚假的应用程序委托。 委派的虚假应用只是对不执行任何操作的真实应用代表的空替代。 当启动应用程序时,检测目标是否与单元测试的目标匹配,如果是,我将实际的应用程序代表替换为伪造的应用程序代表。 因此,每次运行测试时,该应用程序都不会启动实际的View Controllers / Views。 这种方法允许以更隔离的方式运行单元。 这样对单元测试的运行时性能有好处,并且不会导致意外的后果。 单元测试中使用存根和模拟。 查看项目代码,您可以找到一个带有存根的文件夹,另一个带有存根的文件夹。 依赖注入 在单元测试中,可以用实物代替存根和模拟物,以具有更可控的环境。 当您在ViewController中执行tableView.delegate = self时,您将使用依赖项注入,因为委托是MapKit实现中的依赖项,因此您正在注入它。 是的,即使您还没有注意到,您多年来一直在整个UIKit进行依赖项注入。 代理和数据源隐藏在协议后面。 对于存根,程序员可以使用接口/协议以及带有覆盖的依赖项或子类的注入。 当然,接口选项比子类更好,因为它导致较少的耦合。 假设您有一个从A类继承的B类,而您的目标是测试B类。可以创建一个从B类继承的C存根。这里的主要问题之一是B类已经带来了很多A类行为,很难摆脱它们(或者它的僵化或不可预测性)。 您想要做的只是保持相似或可代表但可控制且可预测的行为。 为了进行依赖注入,我使用了一个名为Swinjec t (https://github.com/Swinject/Swinject)的第三方库。 没有第三方库也可以建立依赖关系。 但是,Swinject将依赖项存储在Container中,因此更容易将依赖项维护在中央位置,从而提高了代码的整洁度和凝聚力。 要获得实例,您可以执行以下操作: 让person = container.resolve(Person.self)! 这样,您无需在实例化时传递参数。 注册依赖项时传递参数。 使用Swinject,甚至可以将类转换为Singleton,而无需触摸该类的文件(开放式封闭原理)。 Swininject resolve方法尝试获取与特定协议关联的实例。 在单元测试中,我们使存根实现特定的协议(在实际应用中由实对象实现),并通过使用register方法将存根与协议相关联。 注入依赖关系允许更大的责任分离,有助于遵守Liskov原则,Open Close原则和依赖关系反转原则。 […]

Swift中的TDD简介

我的第一篇英文著作,所以开始吧begin 在本文中,我将尝试向您介绍TDD(测试驱动开发)。 那是什么 TDD是一种软件开发技术,仅要求您先编写测试,然后编写实际代码才能通过您之前编写的测试。 如果您的代码必须处理其他情况,或者要向软件中添加新功能,或者要修复该错误,那就很简单。 只需再次编写不同的测试,然后编写代码以再次通过该测试。 因此,如果您实施TDD,则您的开发周期将像这样 编写并运行测试 编码 重构 重新运行测试 如果您的开发阶段尚未完成,请返回第一。 使TDD更快捷 在您的Swift项目(iOS应用程序或macOS应用程序)中实现TDD非常简单。 您将只编写从XCTestCase类继承的类,定义您的测试用例,然后运行该测试。 我将向您展示如何进行测试,在本示例中,为简单起见,我将使用Xcode Playground进行所有这些测试。 在此示例中,我将创建解决该简单问题的算法。 您是游戏开发人员。 您将构建一个简单的格斗游戏,其中涉及2名战士互相斗殴。 每个战士都可以攻击他的对手,并拥有生命力计和伤害力。 如果战斗机受到攻击,他的生命量将因对手的伤害力而减少。 示例对象 塞纳570 60 RKO 510 65 第1栏是战斗机的名称,第2栏是战斗机的寿命表,最后一个是战斗机的伤害力 步骤1 定义战斗机 为了使您的代码通过该测试,您必须在Player类上修改attack(opponent:) 霍雷! 您的测试已通过

iOS单元测试

基本上,单元测试的思想非常简单: 了解输入并将输出与期望值进行比较,我们可以验证黑盒是否正常工作。 + ———————- + 输入| | 输出量 + ———-> +黑匣子+ ————> | | + ———————- + 尽管经常知道单元在您的项目中实现了什么功能,但是对于单元测试环境仍然未知,这就是为什么在这里使用Blackbox术语的原因。 但是什么是单元测试单元? 嗯,有很多定义,但是它们都以一种或另一种方式将Unit定义为功能单元,我想说是不可分割的功能单元 。 在Swift语言中,功能的不可分割的单元是一个功能。 每个函数都可以带有一些参数(输入),可以返回某个值(输出),因此可以通过单元测试来验证其功能。 您可以编写一个涉及整个类型(类或结构)或什至组合成一个模块的几种类型的单元测试,但是: 将一个单元测试与一个功能绑定在一起可以使您的测试尽可能地适应代码。 好的,让我们玩一下Swift编程吧。 因此,当您具有如下方法或函数时,单元测试的想法非常简单: 为了验证它是否正确运行,您编写了一些传递已知Params代码,并检查返回的Result是否与这些参数所期望的一样。 这个特殊的代码称为单元测试,因为它测试一个单元doSomeWork函数。 这是一个精致的示例,在现实世界中, doSomeWork函数而不是Class或Structure的方法,因此其工作可能取决于其他方法和属性。 尽管通常依赖另一种方法并不危险,但是应注意依赖属性和实例变量,因为它们描述了类型的状态并且在测试之间是持久的,此处的规则: 使您的方法成为函数式编程的纯函数 。 纯函数是不依赖所有者状态且不会产生副作用(更改所有者状态)的函数。 像许多其他现代语言一样,Swift也是面向对象的,它具有具有属性和实例变量的类,这使得不可能将所有方法都当作纯函数使用,特别是如果它是UI应用程序。 万一该方法无法重构为纯函数,您的规则是: 尽可能减少方法对其所有者状态的依赖 使用纯函数(除了使代码可测试以外)的另一个有用效果是,它们将函数式编程的优点引入了您的应用程序。 现在可以安全地构建方法链,并且对于相同的输入参数,其结果将始终相同。 它还通过增加可扩展性和减少耦合来改善应用程序体系结构。 回到无法摆脱对状态的依赖的方法,可以依靠的一种提高其可测试性的技术是依赖注入 , 考虑下一节课: 在这里, Jobs将在doSomeWork方法中执行实际工作的Jobs委托给ConcreteWorker ,由于以下原因,我们不能删除使doSomeWork不利于测试的依赖关系: ConcreteWorker可能依赖于在测试过程中难以设置或无法设置的环境,例如,它可能会使用Simulator无法使用的iOS功能 Jobs可以开始使用ConcreteWorker的子类来覆盖work(:)方法,或者甚至可以在运行时选择适当的工作器 这些是以下事实的结果: Jobs控制了ConcreteWorker实例,而无法从外部进行管理。 为了解决这个问题,我们将使用依赖注入来反转此控件: 请注意,我们还引入了Worker协议,该协议为Jobs使用的工人定义了一个合同,该合同将其与具体的worker实施分离。 单元测试还有另一条规则: 将测试与测试的类隔离开 […]

Swift — 4 —核心数据—第4部分编写单元测试用例

嗨,我回来了,我最喜欢的主题核心数据带有单元测试😀。 希望您对编写单元测试用例有一些基本的了解。 我们将使用XCTestCase类和XCTest框架来编写单元测试并对其进行测试。 源代码在本教程的底部。 单元测试对于测试代码非常有用,而无需在设备或模拟器上启动代码并转到特定的视图控制器。 我们可以非常快速地测试所需的代码,而无需在设备/模拟器上运行。 它将节省大量的开发时间。 一旦您采用了单元测试,它将激发您按照MVVM,依赖注入和适配器模式等编写小的可测试代码。 我将继续第3部分。请从此处下载第3部分源代码。 我在CoreDataManager类中添加了另一个方法。 只需复制以下方法并粘贴到CoreDataManager类中即可。 此方法使用获取请求来获取个人记录并删除它们。 / *在需要刷新人员数据的情况下,可以调用此方法* / func flushData(){ 让fetchRequest:NSFetchRequest = NSFetchRequest (实体名称:“人”) 让objs =试试! CoreDataManager.sharedManager.persistentContainer.viewContext.fetch(fetchRequest) 例如,让obj作为objs中的NSManagedObject { CoreDataManager.sharedManager.persistentContainer.viewContext.delete(obj) } 尝试! CoreDataManager.sharedManager.persistentContainer.viewContext.save() } } 现在,让我们开始设置单元测试: 我们还需要在测试目标中添加PersonData.xcdatamodeld。 请参考所附的屏幕截图,并仔细查看右侧底部的Target Membership: 让我们复制下面的代码,并将其粘贴到PersonDataTests.swift中。 它看起来应该像这样: // // PersonDataTests.swift // PersonDataTests // //由Alok Upadhyay在18/3/28创建。 //版权所有©2018 Alok。 版权所有。 // 导入XCTest 导入CoreData @testable导入PersonData class PersonDataTests:XCTestCase […]

使用单元测试测试您的UI

概括地说,我们编写的测试分为3个不同的类别: 单元测试是人们可以编写的最简单的测试,并且顾名思义,它可以独立测试单个代码单元。 它们既快速又稳定,我们可以编写很多它们,它们是其他测试的基础。 一个普遍的误解是,单元测试仅针对模型(即处理数据的实体)编写。 另一方面, UI测试是对整个事物进行整体测试的工具-它们从用户角度检查系统,并且极易损坏。 UI测试既昂贵又缓慢,并且是需要不断进行维护以跟上不断发展的系统的测试。 我们应该始终努力减少运行测试所需的时间-通过将大量的UI测试转移到单元测试,我们可以获得绝缘和速度方面的好处。 这是一个例子…… 假设我们在UIViewController中有一个UITableView,它应该覆盖整个视图。 我们的测试不应检查tableView是否正确“拉伸”(最终结果),而应检查视图是否具有上述约束(并相信系统在内部做正确的事情): 这个简单的测试比任何UI测试都快得多,并且不依赖于应用程序的其他部分来加载和显示View Controller(根据UI测试的要求)。 希望这可以帮助。 测试愉快! 为了完整起见,这是整个TasksViewControllerTests类:

TDD迅速UygulamaGeliştirmeninfaydalarıve Xcode da Swift迅速XCTestkullanımı

测试驱动开发(TDD)可以帮助您完成测试驱动开发(TDD) 。 Genellikleyanlışbirkanıolarak单元测试yazmanınuygulamageliştirmesüresiniuzatacağıdüşünülüyor。 Halbukiuygulamamızdaöncedenunit testleriyazılmasıdaha kolay birşekildeyapılacakişlevintasarlanabilmesinisağlar。 布达下摆tasarım下摆işlevintestinde bize zamankazandırdığıgibi kodu daha iyiyapılandırabilmemizede olanaksağlar。 Bu nedenle unit testler ile uygulamageliştirmemizdaha sonraoluşacakmuhtemelbuglarıdaöncesindetespitedebileceğimiziçinuygulamageliştirmesüresiniazaltacaktır。 Uygulamamızınkod kalitesiniartırarakdaha az bir bug ilegeliştirebiliriz。 单元测试器下摆pozitif下摆negatif caseler ile beraberyazdığımızdaolabilecekbirçokfarkli案例测试etmeolanağınakavuşmuzoluruz。 Buda iyi testedilmişve daha az bugiçerenkodyazmamızısağlayacaktır。 Yazdığımız单元测试仪daha sonra uygulamadakioluşabilecekdiğerbuglarıtespit etmemizde bizeoldukçakolaylıksağlayacaktır。 Budauygulamamızdakigelecek olanbuglarıçözmesüremiziönemliölçüdeazaltacaktır。 Teamcity gibi CI( Continus Integration)单元测试人员, Jenkins单元, 测试系统(SUT)(测试中) prensibine uygunsürekliolarak测试,edilen biruygulamamızolur。 Projedeyapılan她的承诺sonrasındaunit […]

使用SKProduct测试Swift代码

故事 旧的可信赖的源代码-很好,它可以正常运行很多年。 是的,只有很少的无法解释的方法,没有人记得这是什么以及拥有它们的原因是什么。 还是那是一个错误修复程序? 与其保持原样,而且完全不要触摸它。 上周,我不得不重构一个古董应用程序内购买经理—用Objective-C编写的应用程序非常尊重您的要求,60%包含单元测试。 为了减少任务的繁琐性,我为自己找到了另外一个动机–不仅重构了旧代码,而且还用Swift重新编写了代码。 现在,这项任务听起来更具吸引力。 问题 如果您对主题非常了解(Apple应用程序内购买),那么编写具有基本功能的应用程序内购买经理(控制器/帮助者/其他)很容易: –从AppStore检索产品信息(标题,描述,价格等) –在App Store上购买产品 – 恢复购买。 我跳过了有关购买的冗长的基本解释,直接解决了这个问题:以前的Objective-C实现具有良好的单元测试范围。 OSMock框架实际上可以模拟所有内容,包括必要时的系统类。 例如,以下代码创建一个SKProduct类的模拟,并为productIdentifier方法创建一个存根: 静态NSString * productIdentifier = @“ com.test.identifier”; id产品= [OCMockObject mockForClass:[SKProduct类]]; [[[product stub] andReturn:productIdentifier] productIdentifier]; 再举一个例子,下面的代码允许我测试价格处理: id产品= [OCMockObject mockForClass:[SKProduct类]]; NSDecimalNumber * price = [NSDecimalNumber decimalNumberWithString:@” 1“]; [[[产品存根]和退货:价格]价格]; NSLocale * locale = [NSLocale localeWithLocaleIdentifier:@” en_US”]; [[[产品存根]和Return:locale] priceLocale]; 新代码应在Swift中。 此代码也必须是可测试的。 如何在Swift中模拟SKProduct类? […]

如何在iOS Xcode中使用Alamofire实施单元测试

在开发iOS应用时,我们应该为这些方法编写单元测试。 编写单元测试可确保所需功能正常运行。 因此,让我们开始如何实现它。 在本次会议中,我将Alamofire用于网络请求,将SwiftyJson用于数据解析。 我已经使用CocoaPods安装了这些库。 您可以参考以下链接https://stackoverflow.com/questions/36084956/integarte-install-cocoapods-to-existing-xcode-project-objective-c-or-swift进行CocoaPods安装。 因此,使用Alamofire进行网络调用的代码将是,我已将此代码添加到名为HttpClient.swift的类中 Alamofire.request(apiURL,方法:方法,参数:参数,编码:编码,标头:customHeader) .responseJSON { debugPrint(“ \ n \ napi响应>>> \(String(描述:response.result.value))\ n”) 切换 (response.result){ 案例 .success( _ ): 如果 让数据= response.result.value { 让 json = JSON(数据) completeHandler(json, nil ) 自我 .handleSessionExpiredForResponse(json:json) } 情况 .failure( _ ): 如果 response.result.error!= 无 { debugPrint(“ \ n \ nresponse.result.error!>>> \(response.result.error!)\ n”) completeHandler( nil ,response.result.error […]

如何在iOS测试中使用Chaos

您听说过无穷大的猴子定理吗? 内容如下: “如果您让猴子在打字机上敲击随机键,在无限的时间内,它会扑灭任何预期的书面文字” 还有其他版本使用无限数量的猴子而不是时间。 作为应用程序开发人员,我们有类似的情况。 我们的应用程序被大量以无法想象的方式使用我们的应用程序的用户使用(好吧,他们被编号了,而不是猴子,但是仍然相似。) 结果,他们将发现我们从未预料到的错误。 那么我们该怎么做呢? Netflix创造了一个新名词:混沌工程。 这样做的目的是在生产代码中引入混乱,以发现系统在出现故障时的行为。 他们发表了他们的混乱猴子,杀死了他们生命系统的实例。 我们也许可以在后端系统上使用它,但是总的来说,我们的应用程序有其自身的问题,我们不能仅仅在用户使用它们时就杀死它们;)。 所以,我们能做些什么? 单元测试 在为算法编写单元测试时,通常必须减少输入值,因为它们可能很大。 相反,我们考虑一些极端情况并进行测试。 可悲的是,这可能导致我们遗漏了一些错误。 考虑到上述在测试中添加受控混乱的想法,我们可以开始使用特定范围内的随机值。 这些值需要生成,并且我们的算法必须包含一个条件(意味着条件始终为真)。 幸运的是,我们不必自己写所有这些随机性。 SwiftCheck已创建为我们做到这一点。 让我们开始吧! 阅读以下说明时,请考虑将电子邮件作为我们的域结构: 结构电子邮件{ var local:字串 var host:字符串 var tld:字符串 } 发电机 在SwiftCheck中,您可以使用生成器来创建任意数据。 尝试生成电子邮件地址时,我们必须生成三个部分:本地,主机和TLD。 本地部分可以包括大写和小写字母,数字和某些种类的字符。 完成此操作后,我们可以构建库并以通常的方式包括它们: 迦太基建造 完成所有这些之后,我们可以添加相应的测试: 结论 即使是最优秀的开发人员,也无法预测其应用程序可能具有的所有状态。 这导致没有测试所有内容。 由于没有人是完美的,并且我们无法测试所有内容,因此我们的应用程序容易出现错误。 找到这些错误很困难,但是混沌测试可以提供帮助。 在单元测试中,我们可以在测试算法时测试各种属性。 在UI测试中,我们可以使用猴子来完成应用程序的操作,这是我们从未想到的。 听起来需要做很多工作,但是由于这可以防止生产中的重大错误,因此我认为这是值得的。 如上文所述,它甚至可以用于查找生产中的错误。 如果您感兴趣,它是UIWebView。 从iOS 9开始,似乎每个导航都会创建很多手势识别器。 过渡到其他页面时,这些页面不会被释放,因此会保留下来。 当您触摸屏幕时,所有这些动作都会使主队列超载,从而使您的应用无响应。 最后,看门狗杀死了它。 上一篇:黄金大师测试

单元测试和太空救援任务

我看到无数人鼓吹说单元测试对任何软件项目都非常重要。 实际上,只有极少数人使用令人信服的示例来支持自己的论点,这使得单元测试看起来像是可悲的旧式教条,可以放心地忽略。 人们是榜样的创造者,直到看到一个榜样,我们才会真正相信某些东西。 今天,我再次提出这个论点:单元测试对于确保代码按预期工作至关重要。 单元测试似乎没什么用,因为在您第一次编写代码时,它确实不是很有用。 但是我们不是生活在一个静止的世界中,我们生活在一个市场中,业务逻辑可以在几个小时内多次变化,我们生活在一个明天我们可以与今天的朋友展开战争的世界中。 不断变化的环境意味着不断发展的代码库。 几乎可以肯定,您会回到软件的一部分并对其进行修改,以执行一些不同的操作,尽管有些不同。 更改发生时,单元测试确实会发光:当新更改意外破坏应用程序的其他部分时,它将通知您。 为了支持我的观点,我邀请您进行一次遥远的冒险。 2148年,整个地球都震惊了一个令人震惊的消息:火星人的住所已被我们致命的太空敌人-虫族(Zerg)接管。 我们所有的火星定居者都被劫为人质。 太空级武器制造商Space Force的CTO感到恐惧和愤怒,决定部署一支由您最先进的外星战斗机器人组成的机队,这是世界上最史诗般的太空救援任务之一。 您迅速地慢跑到计算机上,开始键入一个程序,凶猛的机器人机群将在该程序上运行。 您选择了一个多世纪以前开源的编程语言。 当您的机器人到达时,火星将有两种:人类和虫族。 因此,您需要两个模型来表示它们。 由于它们都是有机的,因此您将创建两个符合相同Bio协议的结构。 Bio协议规定,任何Bio单元都将具有两个共同的属性:它们将具有一定数量的腿和体温(以摄氏度为单位)。 根据一直研究这种残酷外来物种的贵公司精英科学家的说法,每个异虫都有8条腿,它们的体温为27摄氏度。 协议Bio { var bodyTemperature:Double {get} var numberOfLegs:Int {get} } struct Human:Bio { 让bodyTemperature = 37.0 让numberOfLegs = 2 } struct Zerg:Bio { 让bodyTemperature = 27.0 让numberOfLegs = 8 } 现在,您已经定义了所需的模型,接下来继续向复杂的Zerg杀人机中添加一些代码。 根据您从科学人员那里获得的数据,您可以自信地编写机器人的逻辑代码。 机器人上的高级计算机视觉算法和热传感器可以轻松识别生物的腿数和体温。 因此,您继续创建基于这些属性来确定生物是朋友还是敌人的方法。 在这种方法中,您需要检查以下内容:如果一个生物单元的两条腿以上或它的体温低于30摄氏度,那么消除这种恶心的异虫。 […]