从踢弟弟到逼弟弟

tdd_bdd.jpg

此文为「Enrico」同学的原创投稿,非 Tommy 出品。本文原作者之观点不代表本人及本网站的任何观点,仅代表其个人或组织,特此声明。

自从 2015 年 10 月十八届五中全会决定全面放开二胎,一对夫妇可以生育两个孩子。身边不少头胎生了女孩的夫妻积极响应国家号召,生下了第二胎,而且大部分人二胎生的都是男孩,凑得个好字,儿女双全。

但是,有人的地方就有江湖,有江湖的地方就有竞争。许多头胎的孩子,本来颠颠高高兴兴,万千宠爱在一身。突然一天,爸爸妈妈告诉他们,要当哥哥/姐姐了,懵懂的他们还不知道是什么回事。直到弟弟/妹妹降生,全家人注意力迅速转移,哥哥/姐姐们才知道歌里唱的「由来只有新人笑,有谁听到旧人哭」是什么意思。小小年纪开始了体会怨憎会、求不得之苦。

若是二胎是弟弟,自己是女儿身,而又生在那重男轻女之家,那苦真叫作苦过弟弟。老大心理上如不能适应,则有可能对弟弟下手,各种踢弟弟。然而小孩动手不知轻重,踢弟弟容易有伤痕,被大人发现了那可是胆汁送黄莲,苦上加苦。于是,扭曲的小孩另寻方法,改为心理上摧残弟弟,从踢弟弟转为逼弟弟。

国外就有一些小孩,早早就从踢弟弟转为逼弟弟。他们就是

敏捷开发工程师

他们搞了个敏捷宣言

Individuals and interactions over processes and tools

Working software over comprehensive documentation

Customer collaboration over contract negotiation

Responding to change over following a plan

That is, while there is value in the items on the right, we value the items on the left more.

简单来说是加强沟通,忙尽快出能工作的软件。

目的是为了摆脱过去那种大型项目开发的中因为因循繁琐的流程、维护大量的文档所造成的低效率,从而适应项目小型化,拥抱需求变化,营造一个欢乐开发的海洋。

为了实现其中的第二点,尽快提供工作的软件 (Working software),有些小孩又搞了一个敏捷实践,叫做

踢弟弟 (TDD Test-Driven Development)

他搞了几个原则

write a “single” unit test describing an aspect of the program

run the test, which should fail because the program lacks that feature

write “just enough” code, the simplest possible, to make the test pass

“refactor” the code until it conforms to the simplicity criteria

repeat, “accumulating” unit tests over time

概括来说,是先写一小段某个功能的测试代码,测试失败,再写实现代码,测试成功,再迭代下一个功能。

这对于单元测试与开发是很有用的一种实践。因为踢弟弟是要求在写代码之前就要想好怎么测,测什么,这解决了可测性低的问题。另外,踢弟弟还可以提高代码的测试覆盖率,令 bug 在编码阶段就能被发现。减少上线后发现问题,修复问题的指数级增长成本。

然而,踢弟弟也有它的不足。

  • 它解决的是代码级的验证,但是测试代码与需求的符合问题解决得不是很好,非技术人员、客户看不懂代码,无法评审测试是否符合需求。

  • 测试代码可能写得太大或者太小,令开发人员效率下降。这与测试代码与功能对应不起来有很大关系。

于是,又有一些小孩扩展了踢弟弟,提出了

逼弟弟 (BDD Behavior-Driven Development)

他们发现,如果将自然语言按照一些简单语法组织起来,代码将会非常容易解释与处理。使用这种方法可以让非技术人员、客户可以参与到需求的确认与验收当中。

我们看一下两个例子

1
2
3
4
5
6
7
8
9
10
Scenario: Refunded items should be returned to stock
Given a customer bought a black sweater from me
And I have three black sweaters left in stock.
When he returns the sweater for a refund
Then I should have four black sweaters in stock.
场景:微信聊天
假如 手机安装了微信
当 用户打开微信
那么 手机会出现用户的微信聊天界面

以上就是逼弟弟使用的叫做 Gherkin 的语言,它的理念是使用自然语言来描述功能,而且强调的是使用例子来说明需求功能。是不是跟敏捷开发中的用户故事 (User Story) 很像?嗯,因为它们都是一个妈生的。

其实只要我们回顾一下敏捷宣言,就会发现,逼弟弟干的事就是解决个体之间互动与客户协作这两个问题。

逼弟弟的需求研讨会 (Specification Workshops)

那么,我们使用这种语言,把需求一个个用例子列出来,客户/产品、开发、测试三方一起讨论与确认。

逼弟弟的由外而内的开发模式 (Outside-In Development)

然后,开发人员使用 BDD 工具 (JBehave, Cucumber, Behave) 去运行、实现测试脚本。再一点点编写实现功能代码,走到所有的功能都运行通过。

由于开发的过程是从最接近用户的 UI 界面开始,再想到内部设计,因此它称为由外而内的开发模式。如果再加上由内而外的过程,嗯,《开发人员的自我修养》就等着你来写了,你叫史坦尼斯拉夫斯基对吧?

回到 Gherkin 语言,我们上面提到它需要遵循一定的简单语法:

  • Scenario (场景),说明功能的例子
  • Given (假如),构造测试的环境条件
  • When (当),给予的输入,可以是用户,也可以是外部系统,也可以是系统本身定时/条件触发的
  • Then (那么),系统的输出,或者说行为

若干个 Given, When, Then 构成一个 Scenario,若干个 Scenario 构成一个 Feature,若干个 Feature 最终构成一个系统的完整功能需求。

逼弟弟的不足

由于自然语言的天生缺陷,光用文字总会有一些歧义会产生。例如

1
2
冬天能穿多少就穿多少
夏天能穿多少就穿多少

Gherkin 语言不能完全解决自然语言的歧义问题,如果有图片,那会很有帮助。然而,现有的 Gherkin 语言与BDD工具并不支持插入图片。

碰壁

那你问我资不资瓷,我当然资瓷啊。你们啊,要努力提高姿势水平。国外那 Stack Overflow,水平不知道比你们高到哪里去,我跟他们谈笑风生。

支持的办法就是将 Gherkin 语言与 Markdown 语法 crossover,用 Markdown 标签来插入图片。就像这样:

1
2
3
4
5
场景: 微信聊天
假如 手机安装了微信
当 用户打开微信
那么 手机会出现用户的微信聊天界面
![微信聊天界面](wechat.jpg)

但是由于 Markdown 不支持图片尺寸定义,图片不能缩放,效果可能就会变成

Markdown 图像

图片会显得很大,一页都显示不完,体验很差。

有人提出使用

1
![微信聊天界面](wechat.jpg =320x)

这种办法,可惜的是,不是所有工具/网站都支持。

又有人提出另外写一个 CSS 文件来解决这个问题,但是 Stack Overflow 上很多人表示,另外维护一个文件很不友好。而且,在逼弟弟这个事情上,让非技术人员去写 CSS 更加不切实际。因此,我暂时推荐的解决办法是在 Markdown 中使用 HTML 标签 <img>

就像这样

1
2
3
4
5
场景: 在搜索框搜索目的地
假如 用户在手机上登录了手机助手
当 用户在手机助手上选择导航
那么 手机助手进入导航页面
#<img src="./Main.jpg" width="240">

效果就会是这样

HTML 图像

这里有个 tricky 的地方,就是 HTML 标签前面要用井号注释起来,这样写的好的 Markdown 文件,只要将后缀名从 .md 改为 .feature,就可以使用各种 BDD 工具运行解释,不影响运行啦。

天才

附一

使用 Markdown 编辑工具 MacDown 查看插入图片的效果



附二

使用 Python BDD 工具 Behave 运行插入了图片的 Feature 文件的效果



Tommy Lau wechat
欢迎您扫码订阅我的公众号