Tag: tdd

我在一个月的TDD中学到了什么

很长一段时间,我编写程序时都没有编写任何测试线。 实际上,我认为编写测试是在浪费时间,直到我开始在一家公司工作时,我的工作是为编码不良的应用创建新功能。 每次编写新行时,我都会对该应用程序进行一百次修改,以确保它仍然可以正常工作。 这让我意识到测试非常重要,但是即使考虑到这一点,我也没有给出正确的价值。 有一天,我的同事提出了一个好主意。 让我们以一个新的流程开始这个新项目,让我们使用他说的TDD。 但是TDD到底是什么? TDD是一种鼓励程序员在编写代码之前编写测试的软件过程。 测试驱动开发 ( TDD )是一种软件开发过程,它依赖于非常短的开发周期的重复:将需求转换为非常具体的测试用例,然后对该软件进行改进以仅通过新的测试。 在那一刻之前,我还没有编写很多测试代码,我知道这个TDD决定会让我遭受痛苦。 我将学习测试,新的库以及最重要的内容:如何编写良好的测试代码。 我们做了两周的配对编程,所以我可以习惯TDD及其生命周期,即:编写测试代码并运行。 它可能会失败,实际上,它应该 会失败 ,现在您可以编写一些代码,但仅需最少的代码即可通过测试。 再次运行测试,它应该 通过 。 现在,您可以编写更多测试或重构以前的代码,而无需添加新功能。 在这一挑战中,我们决定使用一些库来帮助我们在iOS中编写测试:Quick,Nimble,Nimble + Snapshots和KIF: 快 GitHub – Quick / Quick:Swift(和Objective-C)测试框架。 快速– Swift(和Objective-C)测试框架。 github.com 帮助您编写测试,为beforeEach和测试本身定义各种测试的上下文。 使测试更具可读性很有用。 敏捷 GitHub – Quick / Nimble:Swift和Objective-C的Matcher框架 Nimble –一个用于Swift和Objective-C的Matcher框架 github.com 使您的断言也更易读,而不是使用XCTAssertEqual(1+1,2,”expected one plus one equal two”)您可以编写XCTAssertEqual(1+1,2,”expected one plus one equal […]

iOS测试技巧#1-跟踪辅助功能测试失败

这是一系列小技巧的第一篇,这些技巧可以帮助您提高测试技能。 您可以在此处查看其他帖子: iOS测试技巧2 –提高测试的可读性 尽管对于代码重用而言很重要,但是断言的帮助器功能可以帮助我们创建更具可读性的测试。 在此特定测试用例中使用辅助函数的原因不在这里。 如果您在测试函数之外执行断言,但测试失败,则Xcode仅在调用断言方法的位置显示消息,如下所示: 发生此现象的原因是函数XCTAssertEqual具有两个默认参数,它们指定文件和执行测试的行。 对于您的项目而言,这不是一个大问题,但是如果您使用的是TDD( 测试驱动的开发 ),则很可能会减慢您的开发速度,因为每次发生多个错误时,您都必须进行调试才能找到出了什么事。 要对其进行修复,这非常简单,您所要做的就是传递给XCTAssert测试的#file和#line ,如下所示: #file和#line是文字值,它们对于日志记录也很有用。 PS:这也发生在Nimble中,因此您可以使用相同的方法进行修复。 所有人,谢谢您的阅读!

Swift中的单元和集成测试技术

单元测试 :是软件测试的级别,其中测试软件的各个单元组件。 目的是验证软件的每个单元是否按设计执行。 集成测试 :是一个软件测试级别,其中各个单元组合在一起并作为一组进行测试 。 此测试级别的目的是暴露集成单元之间交互中的错误。 测试网络请求 让我们看一个示例,该示例显示了使用UrlSession简单网络请求,如图1所示。 这些是我们在此代码中执行的任务 创建一个URL以获取资源 使用默认configuration创建一个dataTask对象 解码从服务器获取的响应 图2显示了符合Codable协议的响应模型struct ,否则.decode将引发error 如上图1所示,我们有三个任务单元。 通过将这两种方法组合在一起,我删除了此代码的可测试性。 我们可以打破这些任务,如图3所示 首先,我们将url请求创建逻辑分离到单独的单元,以便可以分别测试 其次,我们将解析逻辑分离到一个单元,以便该单元可以独立测试 单元测试请求的创建和解析 在这里,我们仅通过制作示例并放入url,将其传递到方法中,并对其返回值进行断言来测试makeRequest方法。 同样,我们可以通过传递一些模拟JSON并对所解析的结果进行断言来测试响应解析。 为了避免do catch块,我们使用了XCTest throws功能 集成测试网络电话 在进行任何集成测试之前,我们首先使逻辑通用。 正如我们所看到的,我们的网络api类仅特定于一个url,并且我们可以使用通用功能来消耗任何种类的url和响应负载 如您在图5中看到的,我们创建了一个APIRequest协议 如图6所示,我们创建了使用请求类型和urlSession实例初始化的APIRequestLoader类。 由于apiRequest应该符合APIRequest协议,因此我们可以访问makeRequest和parseResponse方法。 如图7所示, GitHubRequest实现了APIRequest协议的APIRequest和parseResponse方法,我们将RequestDataType为String并将ResponseDataType设置为MyGitHub类型 如图8所示,我们正在使用通用api函数。 如果将其与图1进行比较,将会看到不同之处。 URLSessionDataTask集成测试 如图9所示,我们通过按正常调用方式调用loadAPIRequest方法来测试它,并等待返回结果,但这两者都要求我们的测试必须通过Internet连接运行 我们需要使加载程序类使用伪造的urlSession ,该伪造的urlSession返回一些模拟数据,而不是访问服务器,这也可以提高测试执行urlSession 。 所以问题是我们如何制作假的或模拟的服务器 使用URLProtocol模拟 URLSession为应用程序提供了高级API,以用于执行网络请求。 表示正在进行的请求的对象,例如URLSession数据测试。 不过,在幕后,还有另一个较低级别的API URLProtocol ,它执行打开网络连接,编写请求以及回读响应的基础工作。 URLProtocol旨在进行子类化,从而为URL加载系统提供扩展点。 Foundation为HTTPS等常见协议提供了内置协议子类,但是我们可以在测试中通过提供一个模拟协议来覆盖这些子类,该协议可以让我们对即将出现的请求进行断言并提供模拟响应。 URLProtocol通过URLProtocolClient协议将进度传达回系统。 如图10所示,我们使requestHandler成为静态属性。 我们在此物业网络中提供的内容将作为响应返回。 当我们调用resume方法时, startloading函数将调用,而不是去服务器,它立即返回requestHandler响应。 […]

iOS的Menerapkan MVP

MVP merupakan sebuah设计模式yang merupakan singkatan dari模型视图演示者。 Selain MVP terdapat设计模式Yang Menurut Saya Cukup Bagus,Yaitu MVVM。 Mengapa kali ini saya membahas MVP? Alasannya Adalah karena saya sedang terlibat projek yang menerapkan MVP。 简单! 😀 阿帕杨膜MVP spesial?。 iOS用户界面UIViewController,UIViewController ini biasanya berisi barisan kode campuran antara kode UI,kode bisnis,dll sehingga membuat UIViewController berisi banyak sekali barisan kode。 MVP成员membuat pemisahan […]

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’,’〜> […]

使用TDD构建JSON解析器

最近,一个朋友需要解析具有自定义格式的文件,并问我如何实现该文件。 我的脑海立刻被我上大学的记忆所淹没。 因此,我回答了他,我首先将制作一个词法分析器,该词法分析器将使用文本文件并生成令牌,然后创建一个状态机,该状态机会将令牌作为状态转换的事件,并且每个转换都会建立一点点解析的结果,直到达到终端状态为止。 我对TDD感兴趣已经有一段时间了,但是没有机会尝试一下,因此这看起来像是一个制作东西的好机会。 在本系列文章中,我将尝试使用TDD创建JSON解析器,并尝试在每个步骤中共享我的尽管流程。 让我们一起学习🙂 在我们开始之前,有一些想法: 我将生产代码和测试代码都写在一个文件中,以便于在它们之间进行切换。 我们将从仅解析没有空格和换行符的JSON文件开始。 我们稍后会处理。 我知道Foundation库有一个非常优化的JSON解析库,我们在Swift 4中添加了Codable协议,涵盖了JSON解析,但是我主要是作为练习来做的。 对于那些不熟悉的人,我将尝试遵循TDD的三个规则: 除非要通过失败的单元测试,否则不允许编写任何生产代码。 您不能编写任何足以使单元测试失败的单元测试。 而编译失败就是失败。 您不能编写任何足以通过一项失败的单元测试的生产代码。 我们首先创建带有单元测试的可可触摸框架项目,然后删除默认测试 对于最退化的情况,第一个测试应该始终是。 因为我们没有在任何地方传递可选参数,所以我们可以创建的最简陋的字符串是空字符串。 现在,我们只说如果解析失败,则返回nil。 现在我们处于红色阶段,我们必须通过此测试通过以使其再次变为绿色。 第一个错误是JSONParser不存在。 因此,我们创建了它。 让我们上一堂课。 现在,对parse方法的调用不存在。 我们可以通过创建一个接受字符串并返回Any的方法解析来解决此问题。 。 空字符串应该失败,因为它不是有效的JSON,因此我们只返回nil 。 在JSON RFC中,它表示JSON可以是以下之一: Boolean , Number , String , Array , Object 。 让我们从最简单的解析器开始:一个布尔值。 首先,我们将解决真实情况: TDD通过最简单的方式产生更好的结果,因此让我们尝试最简单的方法来验证值是否为true 。 这次我们有机会在测试代码中进行一些重构 。 解析器分配开始重复,并且可能会在每个测试中重复。 将其移至设置功能似乎是个不错的机会。 然后我们有: 列表上的下一个是处理错误的布尔值。 我们以与真实案例相同的方式进行测试: 我们做了一个if来检查是否正确 。 […]

测试序列的两种方法

未经请求的代码审查。 最近,我正在阅读使用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来纠正这种疏忽,但是我认为添加此范围不在本文的讨论范围之内。 […]

TDD:unit testingasynchronous调用

球员: 我正在开发一个应用程序,并使用unit testing进行构build。 但是,我现在处于需要testingasynchronous调用的情况。 例如, – (void)testUserInfoBecomesValidWhenUserIsBuiltSuccessfully { if ( ![userBuilder userInfoUpToDate] ) { [userBuilder buildUser]; } STAssertTrue([userBuilder userInfoUpToDate], @"User information is not valid before building the user"); } testing这些东西的一般做法是什么? userInfoUpToDate预计将被asynchronous更新。 谢谢! 威廉

如何使用核心数据进行dependency injection

我玩弄使用核心数据来pipe理对象的graphics,主要是为了dependency injection(NSManagedObjects的一个子集需要被持久化,但这不是我的问题的焦点)。 当运行unit testing时,我想要接pipeNSManagedObjects的创build,用mockreplace它们。 我现在有一个候选的方法,就是使用运行时的method_exchangeImplementations来交换[NSEntityDescription insertNewObjectForEntityForName:inManagedObjectContext:]与我自己的实现(即返回模拟)。 这适用于我已经完成的一个小testing。 我有两个问题: 有没有更好的方式来replace核心数据的对象创build比swizzling insertNewObjectForEntityForName:inManagedObjectContext? 我没有进入运行时或核心数据,可能会漏掉一些明显的东西。 我的replace对象创build方法的概念是返回嘲笑的NSManagedObjects。 我使用的OCMock,它不会直接模拟NSManagedObject子类,因为它们的dynamic@property s。 现在我的NSManagedObject的客户端正在与协议而不是具体的对象交stream,所以我返回了嘲讽的协议而不是具体的对象。 有没有更好的办法? 这里有一些伪装的代码来说明我所得到的。 这是我可能要testing的一个类: @interface ClassUnderTest : NSObject – (id) initWithAnObject:(Thingy *)anObject anotherObject:(Thingo *)anotherObject; @end @interface ClassUnderTest() @property (strong, nonatomic, readonly) Thingy *myThingy; @property (strong, nonatomic, readonly) Thingo *myThingo; @end @implementation ClassUnderTest @synthesize myThingy = _myThingy, myThingo = _myThingo; – (id) […]

TDD iOS教程

您好,我正在寻找iOS TDD的非常好的教程,请你帮我! 什么是最好的iOS TDD书,博客 – >教程(我有不同的研究谷歌,但由于我没有足够的知识“iOS TDD”我不知道哪一个是最好的) 提前致谢!