那一晚,我的 BUG 叫醒了我

2020/05/12 TDD 共 1566 字,约 5 分钟
拔菜集

我与 TDD 不得不说的故事,第一篇。

梦见一个 Bug

那一年我还年轻,刚入行没几年也没有白头发。

在一个夜黑风高的凌晨,写了一天代码的我若梦若醒,兴奋的大脑还在检查和推敲自己的代码是否有问题。

突然我想到一个场景,在某个特定的输入下,我的代码的输出并不是如我所愿。我瞬间清醒,两眼一睁掐指一算,果然,是个 Bug。

早上我迫不及待地来到公司,打开电脑,看着屏幕上映着的那双布满血丝的眼睛,有点生气:这特么还让不让人睡觉了?

我转念一想:“验证我的代码对不对”这件事,明明可以用电脑来完成,我却要牺牲睡觉时间用我的大脑来完成,这就是明明工资比我高的原因?

于是我心生一计,干嘛不把这些场景,用网上看到的所谓“单元测试”写下来,每天下班前把这些单元测试一跑,不就可以安心睡大觉了吗?

这是一个改变我码农生涯的伟大的一天。从此,我变成了一个有思想的码农。

旁白

该码农有了两个思想的萌芽:

  1. 对单元测试产生了内在渴望;而不是靠公司的要求等外力驱动。

  2. 不断补充单元测试用例,以及每天下班前跑单元测试,这就是持续集成啊。

有思想的前提是有素质:

  1. 自己写的代码自己负责,绝不交给 QA 负责。

  2. 自己没测完,代码就不算写完。

测试先行

开始写单元测试之后,最大的收获是自信了:单元测试告诉我,我写的代码是可工作的。

也更淡定了:如果有 Bug,一定是某个场景被遗漏了,补一个单元测试就好了。慌毛?

但是我不满足,我想:为什么我要写完代码再去想那些场景呢?

仓央嘉措告诉我:理解需求之后,我写或不写代码,那些场景就在那,不舍不弃,不增不减。

那么我在写代码之前,就把那些场景分别用例子列下来,不是让我写代码时更下笔如有神吗?

这是让我的码农生涯 Great Again 的一天。从此一位叫做 Kent Beck 的大牛,开始走进了我的生活。

旁白

至此,该码农悟到了写代码的精髓,或者说底层逻辑之一:

  1. 需求 = 测试。把需求分解成几个场景,然后实例化这些场景,它们就成为了测试用例。(那时 Spec by Example 这个词还没出现。)

  2. 既然“需求= 测试”,那么完全可以测试先行,走在代码前面。

测试驱动开发

一开始,我的方法是这样的:

  1. 把主要的用例写在纸上。

  2. 写代码。

  3. 把纸上的用例写成单元测试。

慢慢的,对于复杂的逻辑代码,这种方式开始遇到问题:写完代码后,单元测试写起来很费劲,因为场景太多,都覆盖的话单元测试之间不少重复逻辑。不覆盖的话,我是那没素质的人吗?

于是,我稍作调整:代码未全部完成时,就先写几个单元测试,看如何重构代码让单元测试写起来更轻松,然后再继续写代码。

有了频繁重构的习惯,我往 TDD 的方向又移动了一步。实践了一段时间之后,在某一年春节回家的火车上,我带上了一本 Kent Beck 的书,看看这位元芳是怎么看的。

这本名为《测试驱动开发》的薄薄的小书,歪歪斜斜的每页上都写着“TDD”这三个字。我在火车上横竖睡不着,仔细看了半夜,才从字缝里看出字来,满本都写着两个字是“智慧”。

大师写一个 TDD 循环的步伐非常小,小到让人不服:有必要吗?就这么点逻辑,我欻一下全写出来了,你非得一点一点挪?就这还大师?

但我毕竟是一个有思想了好几个月的码农了,我放下身段,反复揣摩,终于领悟到了大师的智慧:小(步伐),是一种能力。

大算个屁,谁没大过,可你小过吗?

旁白

我读到的 Kent Beck 的智慧:

  1. 小步伐是一种刻意练习,这个行业尤其缺“刻意练习”。

  2. 拆解 Task 到几分钟的粒度,是程序员的核心能力,甚至没有之一。

  3. 重构不是吃饭,饿了才吃;重构是呼吸,随时随地。

它们是那么质朴,平淡无奇,可是鲜有人做到。如果有程序员幼儿园,我认为这些都是幼儿园要教的五讲四美。

待续

TDD 打开了一扇门。接下来碰到的问题是:代码是要维护的,测试代码也是代码,随着测试代码越来越多,如何避免我的维护成本也越来越大?

或者抽象点说:

  1. 如何让代码可测?

  2. 如何让测试可读?

可能下回分解。

文档信息

Search

    Table of Contents