推行并实施行为驱动开发(Behavior Driven Development, BDD)已有三周。

  (行为驱动开发,即在编写产品代码前,先将产品功能描述成功能点(Feature),再对其间的步骤进行实现。在代码完成后,用事先写好的Feature对其功能进行验证。我们使用的Feature描述工具是Cucumber,对Cucumber不了解的人,可以先跳到后,我写了一个简单的例子,说明Cucumber的工作方式。行为驱动开发的好处,可以参考前文)

  第一周。

  整天拿着笔记本满办公室跑,走到开发人员的桌子边。

  问:你实现了什么功能?

  然后以Cucumber的格式把他/她的描述写成场景(Scenario)。

  又问:我写下来,你看看,是这样么?

  跟开发人员商定好了功能后,进一步规范文法,将类似的步骤统一成一种表示法,并以数据驱动方式编写所需的输入输出数据。

  再问:当我从这里输入这个值,从哪里拿到输出呢?

  这个时候,注重可测试性,即一个功能是否容易被证实有效或失效。

  上述三个问题结束,生成了规范的功能点(Feature)的描述了。然后,回到自己座位上,为每个Scenario编写具体实现的步骤。

  第二周。

  制作BDD/Cucumber的幻灯片,对小组成员进行知识普及。演示中也听到了很多新鲜的问题,如:为何Given/When/Then可以描述所有情景等。

  第三周。

  组内已经有若干人员边学边进行Feature的编写,而我的大部分时间也都花在了解疑释惑,帮助新人上手,以及如何提供更好的API支持。

  这三周,是我在公司五年里第一次跟开发人员如今紧密的协同工作。一起商量一个程序应该如何工作(behave),如何与用户交互(interaction),如何安全地限制用户输入与友好地提供输出(input and output),重要的,我们要一起决定当该程序完成后,它应该如何表示出自己正有效的工作(testability)。作为一个测试人员,可测试性与输入/输出是我擅长的,而开发人员则擅长编码,一直提醒我要保持测试架构(Fixtures/API)的灵活与易懂。

  虽然这些经历对于传统的项目组或者早已踏入敏捷的团队,可能没有共鸣。但对于任何一个从传统项目向敏捷转换的项目组,都会从中找到似曾相识的影子,所以我从中挑选了一些比较典型的故事,分享给大家。

  第一个故事:你根本是在用现有系统测试你的Cucumber脚本

  这是跟我一起工作了一周的开发人员H对我说的一句话,”你这不是在测试我的系统,你分明是在用我的系统测试你的自动化脚本“。

  在同他一起编写功能点描述时,产品代码已然提交了,H按记忆对功能做了如下描述:把事件A插入数据库中事件表(event_table),把规则A插入数据库中的规则表(rule_table),运行匹配规则后,事件A应该被打上标签,表明它匹配了规则A。

  我整理后,写成了Feature文件,可运行后,事件A并没有匹配规则A。请H现场调试,他告诉我要追加一条记录,事件A必须同时存在于两张数据库表,匹配才能进行。我修改后运行,还是没有匹配。他又再次调试,发现事件A还需要另一张数据库表的支持,才能进行匹配。于是我又一次修改,才算成功。H很郁闷的进行了上述的抱怨,”你分明是在用我的系统测试你的自动化脚本“。

  由于代码变化快,很难保持与文档的一致性。所以我组里开发人员的口头描述成了的功能描述。但是代码一旦完成,开发人员也未必能够完全描述具体的功能,这导致按照需求和功能编写的自动化代码很难与产品代码相匹配。而如果产品代码先于自动化代码产生,那么做修改的只能是自动化代码,而非产品代码,这变成了白盒测试。即按照既有代码的既有功能进行验证,而非验证代码是否满足预期需求。而开发人员的这句话,正说明了,BDD中,规范(specification)与功能点(Feature)必须先于代码完成的必要性与重要性。如果上述故事里,我们先写好Feature文件,那么要修改的,是产品代码。

  在于另一个开发人员C的协同工作时,我避免了这种情况的发生。虽然C的代码也提交了,但是一起编写Feature文件时,我说,“别想着你的代码实现了什么功能,照着它应该实现什么功能来写”,如此一来,写成的20对输入输出的Feature,在运行时,只有10对通过,另外10对,如今的代码并不支持。虽然现在并没有把这个任务加到任务表,但我们已经有了一个基本的认识,这样的功能应该支持何种输入输出,而我们的代码又实现了哪些。