2015-08-30 00:00:00 +0000

BDD行为驱动开发-学习总结

转载请注明出处:http://elijahdou.github.io/

BDD(行为驱动开发),写的很散,多包涵,还是要多学习一些敏捷开发,多用加深理解!!!

改变一个观念

大部分程序猿在开发的时候,都是直接构建业务代码,完成功能需求,而不是去先写测试代码。因为认为写单元测试之类的测试代码会增加两倍以上的代码量,繁琐不堪且不必要。但是在开发大型项目时,测试驱动开发能够有效的规避bug,理清业务需求,还能提高调试的速度。原因很简单,我们在写测试代码的时候要明确业务需求,尤其是业务需求模糊或者模棱两个的地方,我们必须要搞清楚了才能写测试代码;再者就是一键执行测试 检查模块功能的可用性,比常规的调试快多了吧。


对iOS开发而言测试方式的大概路线是单元测试(XCTest)、TDD、BDD。单元测试就是一系列的断言,网上很多资料,可以看一下,测试大型项目或者复杂逻辑时,力不从心。TDD(测试驱动开发)是敏捷开发的核心实践,基本思路就是通过测试来推动整个开发的进行,先把需求分析,设计,质量控制量化,根据需求排除模棱两可的需求,编写测试用例代码,然后根据测试用例编写产品代码,这就是所谓的TDD。

但是当我们使用TDD时,马上会出现一个问题,我要测试什么,只知道TDD测试驱动开发,却不知道测试什么。而BDD(行为驱动测试)从名字上就明确的告诉了我们要测试什么——行为。

什么叫行为:一个类对象的接口定义了其方法和依赖关系。这些方法和依赖关系决定了类对象如何与应用的其他部分交互 以及该类对象的功能,这些就是对象的行为。

那么“行为”怎么理解?

我的理解(纯粹是一家之言,每个人的理解不一样):

  • 第一步,需求分析,先明确要实现的类在业务需求上要实现的功能。依据“功能”,我们就能确定这个类的接口定义和依赖关系,这样我们就明确了该类的行为。

  • 开发者在编写测试代码的时候,要明确自己的定位,开发者是观察者,而类对象是被观察者,开发者要观察类对象的行为,这个行为包含两个方面:非互动行为 和 互动行为。非互动行为 就是不需要观察者与之互动就可以观察到的行为,可以理解为对象对自己施加的行为(可以理解为我们看到的它的长相,这是他自己打扮的结果);互动行为 就是观察者给被观察者一个信号,被观察者回复一个应答。 以UI控件的测试为例,先测试非互动行为,这个UI控件应该长成什么样子(即根据需求设计的UI,如title color等);然后是互动行为(如Button被点击的反应等)。
  • 行为定义包括两个方面,一是 接口定义的方法,二是 与其他类或者部分的依赖关系。测试的时候不要漏掉对依赖关系的测试。一些必须要处理的操作,但是属于内部实现,在当前对象的行为上体现不出来,应该怎么避免测试上的遗漏?这就是属于依赖关系,因为这些操作在当前的测试对象的行为上体现不出来,但是其他地方肯定要依赖这些操作(但这似乎又和不要测试内部实现有点矛盾)。

在iOS开发中,可用的BDD测试框架

OC版本的BDD框架: 基于宏定义和block实现

swift版本的BDD框架

在这三个OC的框架中,前两个最受欢迎,而从github上的star数量来说,Kiwi大约是Specta的两倍,从侧面说明了使用Kiwi的人最多。但是也有好多反应SpectaKiwi好用的,这主要是看个人的习惯和喜好。他们都是对XCTest的封装,所以不能同时集成到项目中使用。这两者简单使用过程来说,最直观的区别就是Kiwi的书写格式是消息发送,而Specta是点语法的链式表达。

Kiwi大约是Specta的主要区别(来源于多篇文章的总结):

  • Specta由github的RAC的那帮人维护,使用较多的黑魔法,一旦apple改变了测试的底层实现,可能会出现很多问题
  • 功能上来说,Kiwi功能要多一些Specta就是没有mock和验证功能Kiwi
  • 集成的方便程度,Kiwi只需要使用Pod集成一个Kiwi,而Specta还需要其他的三方库支持,如Expecta,因为Specta是轻量级的,需要其他框架配合完成更复杂的功能。
  • 使用的方便程度,这个因人而异,前者是OC的消息发送,后者是点语法的链式表达,个人倾向于点语法,整个表达结构和Masonry很相似。Specta的expectation语法有一个比Kiwi好的地方:每个变量都隐式boxing 如expect(items.count).to.equal(5)。不需要像Kiwi那样将5包装成NSNumber,theValue(5),so SpectaExpecta搭配使用效果更好。

Kiwi为例,学习BDD的过程

喵神的两篇文章,从测试的基础讲起,XCTest到TDD再到BDD再到Kiwi的学习使用,先学习这两篇,基本上就了解了BDD的Kiwi测试

接下来这一篇blog,很有价值,以为它用一个实际项目讲解了 RAC MVVMKiwi在项目中的实际使用。RACMVVM是我们下一步要用到的框架。

看完这三篇基本上就了解BDD,还有如下几篇可以看一下:


行为驱动开发大概三个步骤:

  • 选择最重要的行为,并编写行为的测试文件。此时,由于测试对象的类还没编写,所以编译失败。创建测试对象的类并编写类的伪实现,让编译通过。
  • 实现被测试类的行为,让测试通过。
  • 如果发现代码中有重复代码,重构被测试类来消除重复

简单说来,TDD的基本步骤就是“红→绿→大胆重构”。

先编写测试代码。这时因为还没有对应的产品代码,所以测试代码肯定是无法通过的。在大多数测试系统中,我们使用红色来表示错误,因此一个测试的初始状态应该是红色的。接下来我们需要使用最小的代价(最少的代码)来让测试通过。通过的测试将被表示为安全的绿色,于是我们回到了绿色的状态。接下来我们可以添加一些测试例,来验证我们的产品代码的实现是否正确。如果不幸新的测试例让我们回到了红色状态,那我们就可以修改产品代码,使其回到绿色。如此反复直到各种边界和测试都进行完毕,此时我们便可以得到一个具有测试保证,鲁棒性超强的产品代码。在我们之后的开发中,因为你有这些测试的保证,你可以大胆重构这段代码或者与之相关的代码,最后只需要保证项目处于绿灯状态,你就可以保证代码没重构没有出现问题。


测试中的原则,讲几个重要的,可测试 可重用之类的是必须的

  • 注重测试对象的行为,而不是内部实现,就是只测试接口和依赖关系,不测试内部实现
  • 单一性,一个测试文件只测试一个类
  • 小步前进,不要一次写好多测试代码,一起去实现业务,在一起测试
  • 及时重构,对结构不合理或者重复的代码,在测试通过后,应及时进行重构,并再次测试

知乎上有个问题TDD 与 BDD 仅仅是语言描述上的区别么?的一个回答如下:

BDD的核心价值是体现在正确的对系统行为进行设计,所以它并非一种行之有效的测试方法。它强调的是系统最终的实现与用户期望的行为是一致的、验证代码实现是否符合设计目标。但是它本身并不强调对系统功能、性能以及边界值等的健全性做保证,无法像完整的测试一样发现系统的各种问题。但BDD倡导的用简洁的自然语言描述系统行为的理念,可以明确的根据设计产生测试,并保障测试用例的质量。

这个回答提到的,也是我在学习BDD过程中想到的问题,有没有一种驱动测试的方法,能覆盖到所有边界,测试代码可重用性高。 至于性能测试 BDD是不是无能为力了? 看官如果有好的方法,评论一下。