iOS开发中的测试框架

jopen 9年前

来源:CrespoXiao的简书

我们为什么要用测试框架呢?当然对项目开发有帮助了,但是业内现状是经常赶进度,所以TDD还是算了吧,BDD就测测数据存取和重要环节,这很重要,一次性跑完测试单元检查接口或模块的可用性,这比打断点调试强多了吧,至于UI测试就算了吧(xcode7集成了),呵呵。

首先了解一下BDD与TDD的概念:

BDD(Behavior Driven Development),也就是行为驱动开发,它旨在解决具体问题,帮助开发人员确定应该测试些什么。

TDD(Test-Driven Development),就是测试驱动开发,通过测试来推动整个开发的进行。

github上搜TDD或BDD,语言选择objective-c,star最多的就是这2个:

https://github.com/kiwi-bdd/Kiwi/wiki

https://github.com/specta/specta

还有一个测试框架用的比较少,叫Cedar  https://github.com/pivotal/cedar

此外,还找到2个Swift的测试框架,以后用Swift开发可以考虑使用:

https://github.com/railsware/Sleipnir 

https://github.com/Quick/Quick

Cedar,Specta和Kiwi这3个框架就是目前Objc最流行的BDD测试框架了,其中Kiwi最受欢迎(根据github上的star数来推断,行为描述和期望写起来也比较易懂,至少我是这么认为的)。

不过,Specta是 RAC 那帮人维护的,用于测试的黑魔法更多,我担心这帮人精力太分散顾及不上这个测试框架。但是有些使用测试框架经验丰富的人选择用回了XCTest,主要是因为它跟Xcode集成的比较好,而且嫌BDD框架hold不住业务的发展,cmd+u可以一次性跑完所有的测试(试过3个框架目前都可以这样的)。

XCTest与Xcode深度集成,而且可以享受Apple后续对XCTest升级的福利。XCTest 的优势和缺点都是由于它太简单了。你只需要创建一个类,使用 “test” 作为测试方法名的前缀,只需要这样就可以了,不需要再做其他的。不幸的是,这已经是 XCTest 的全部优点了。BDD 框架的附加功能的重要性是取决于项目的大小。我的结论是,XCTest 对中小型的工程来说是一个很好的选择,但是对于更大型的工程,就有必要参考一下像 Kiwi 或者 Specta 这样的 BDD 框架。Specta和Kiwi的区别就是Kiwi包含了Specta和OCmock以及Expeata所有的功能。换句话说Specta就是没有mock和验证功能Kiwi。经测试,Kiwi与Specta是不能同时在项目中使用的,会Crash,不信你试试,不过各自可以与XCTest混合使用因为它们是基于XCTest封装的。

Cedar,Kiwi,以及 Specta 提供类似语法,我不能说其中一个框架要比其他所有都好,它们各有利弊,选择 BDD 框架归根结底来自个人偏好。我选择Kiwi是因为只需要在podfile导入一个Kiwi就行了,Specta则需要依赖别的第三方库,虽然灵活,但灵活有灵活的坏处,当然也有好处,你喜欢就好,反正用得不爽别怨我。

如果使用Specta,还要引入OCmock/OCMockito以及Expeata/OCHamcrest一起配合使用。

OCMock Or OCMockito

这两个都是用来mock对象,Stub方法的,他们之间的区别在于使用OCMock的库比OCMockito的库多,而且文档和教程更加丰富。

Expecta Or OCHamcrest

这两个都是断言的扩展框架,Expecta不成熟,框架还有一些的问题。OCHamcrest更加成熟,而且可扩展性高,可以自定义自己的断言,更灵活。

BDD的理念是你不是在写代码,而是在讲故事。而整个故事是由Given…When…Then组成。我们可以来看看BDD框架Kiwi的一段测试代码:

describe(@"Team", ^{      context(@"when newly created", ^{          it(@"has a name", ^{              id team = [Team team]; [[team.name should] equal:@"Black Hawks"];           });          it(@"has 11 players", ^{              id team = [Team team]; [[[team should] have:11] players];           });      });  });

这个测试用例就是在说Given a Team,When newly created,it should have a name, and should have 11 players,基本上不需要注释就能知道在干嘛。

参考:http://onevcat.com/2014/05/kiwi-mock-stub-test/

不同类型的模拟对象的基本定义:

double 可以理解为置换,它是所有模拟测试对象的统称,我们也可以称它为替身。一般来说,当你创建任意一种测试置换对象时,它将被用来替代某个指定类的对象。

stub 可以理解为测试桩,它能实现当特定的方法被调用时,返回一个指定的模拟值。如果你的测试用例需要一个伴生对象来提供一些数据,可以使用 stub 来取代数据源,在测试设置时可以指定返回每次一致的模拟数据。

spy 可以理解为侦查,它负责汇报情况,持续追踪什么方法被调用了,以及调用过程中传递了哪些参数。你能用它来实现测试断言,比如一个特定的方法是否被调用或者是否使用正确的参数调用。当你需要测试两个对象间的某些协议或者关系时会非常有用。

mock 与 spy 类似,但在使用上有些许不同。spy 追踪所有的方法调用,并在事后让你写断言,而 mock 通常需要你事先设定期望。你告诉它你期望发生什么,然后执行测试代码并验证最后的结果与事先定义的期望是否一致。

fake 是一个具备完整功能实现和行为的对象,行为上来说它和这个类型的真实对象上一样,但不同于它所模拟的类,它使测试变得更加容易。一个典型的例子是使用内存中的数据库来生成一个数据持久化对象,而不是去访问一个真正的生产环境的数据库。

实践中,这些术语常常用起来不同于它们的定义,甚至可以互换。它们自认为自己是 "mock 对象框架",但是其实它们也提供 stub 的功能,不要太过于陷入这些词汇的细节。

下面讲讲Kiwi的使用,尽量简单,以便快速上手,当然详情还是得看官方文档,链接奉上:

https://github.com/kiwi-bdd/Kiwi/wiki

http://onevcat.com/2014/02/ios-test-with-kiwi/

1.Kiwi的安装

podfile中写入

target 'OneTravelTests', :exclusive => true do      pod 'Kiwi'  end

然后pod install或pod update就可以了。

可以安装一个xcode插件http://alcatraz.io,里面有Kiwi的template,安装一下,然后就可以使用Kiwi了。然而有人说插件用不了,怪我咯?懒得解释了,看这里吧:

https://github.com/cikelengfeng/RPAXU

2.Kiwi的基本语法解释

上3张图,一切尽在不言中。

iOS开发中的测试框架

kiwi基本语法解释

iOS开发中的测试框架

mock与stub解释

iOS开发中的测试框架

测试数据请求

3.我们应该/不应该测试什么

BDD 的第一个单词就表明了这一点,你不应该关注于测试,而是应该关注行为。这个看似毫无意义的变化提供了应该测试什么的准确答案:你应该测试行为。

例如你设计的一个对象,它有一个接口定义了其方法和依赖关系。这些方法和依赖,声明了你对象的约定,它们定义了如何与应用的其他部分交互,以及它的功能是什么。它们定义了对象的行为。同时这也应该是你的目标:测试你对象的行为方式。

不应该测试什么呢?

不要测试私有方法:私有方法意味着私有,如果你感到有必要测试一个私有方法,那么那个私有方法一定含有概念性错误,通常是作为私有方法,它做的太多了, 从而违背了单一职责原则。

不要 Stub 私有方法:Stub 私有方法和测试私有方法具有相同的危害,更重要的是,Stub 私有方法将会使程序难以调试。通常来说,用于Stub的库会依赖于一些不寻常的技巧来完成工作,这使得发现一个测试为什么会失败变的困难。

不要测试构造函数:构造函数定义的是实现细节,你不应该测试构造函数,这是因为我们认同测试应该与实现细节解耦这一观点。

不要 Stub 外部库:第三方代码不应该在你的测试中直接出现。

4.测试的目的

使重构更简单 —— 你可以自信的修改实现细节,而不用去触及公有 API。

避免代码恶化—— 恶化在什么时候发生?在你修改代码的时候。

提供了可执行的说明和文档 —— 你在什么时候更想知道软件实际上是如何工作的?在你想修改它们的时候。

减少了创建软件的时间 —— 怎么减少时间的?是通过更快速地修改你的代码,出错时测试会自信地告诉你哪里出错了。

降低了创建软件的代价 —— 时间就是金钱,朋友。

测试应该:

很快速(Fast) —— 测试应该能够被经常执行。

能隔离(Isolated) —— 测试本身不能依赖于外部因素或者其他测试的结果。

可重复(Repeatable) —— 每次运行测试都应该产生相同的结果。

带自检(Self-verifying) —— 测试应该包括断言,不需要人为干预。

够及时(Timely) —— 测试应该和生产代码一同书写。

5.UI测试

关于UI测试,需要测试的是用户的交互,而不是应用的外观,Xcode7中新增了UI Testing,具体可以看wwdc 2015 session :406_hd_ui_testing_in_xcode

好了,没了,我有严重的拖延症,哎。

更多请参考:http://www.objc.io