参与 Element3 开源项目,使用 TDD 的一些感受


TDD的困惑

很多项目团队并没有使用 TDD (测试驱动开发)的开发方式,我想这在目前的开发团队中占比非常高,特别是中小型公司的前端开发团队,几乎可以说是“全军覆没”,为什么?原因可能是以下所列的一些:

  • TDD 太麻烦,增加了工作量
  • TDD 太复杂,普通程序员掌握不了
  • TDD 并不是必须的,不必要投入这些时间成本
  • TDD 是个太理想的方式,还是要面对现实
  • TDD 很好,可是我们公司实力弱,招不起那么专业的开发人员啊

我想这里面有一些可能的原因:

  • 对 TDD 的理解可能还不够深刻
  • 没有给团队一定的空间和时间去尝试和体会
  • 眼光看得不够远,只关注眼下项目的结果
  • 没有真正体会过 TDD 的好处和收益

TDD的思考

还有一个极限编程群里的说法是:

说到底还是开发人员能力不行,没练好 TDD ,说什么也没用,TDD 是要刻意练习的,“先投一万个球再说!”。 😄

总的来说,这个 TDD (测试驱动开发)就跟很多体验性的能力一样,不亲身体验,不深入体验,不会有深刻的感受,正应了现在流行的“真香”定律,总是要用过了,体会过了,才会说好用的。

就好比,有人跟我们说,健身很快乐、很上瘾,收益非常大,而我们却没办法体会,也明知这个道理,也不能实践。然后呢?总是有一堆理由和借口来给别人,也给自己开脱。

  • 健身 太麻烦,太累,每天上班工作已经很累了
  • 健身 太难了(要办健身卡,请健身教练),一般人没条件
  • 健身 也不是第一要务,暂时没那么多时间去投入
  • 健身 的生活方式是理想的方式,目前的现况还是要面对的
  • 健身 很好,可是家里的现状不允许啊

当然,健身还只是一个个人的事情,而 TDD 则是一个团队和一个公司的事情,有时候可能最终能推动会显得更困难。当然,也可以从自身做起,逐渐带动周围的人慢慢加入,这需要一个过程。

然而这个情况在开源社区中,可能就相对好些。一方面参与开源项目的开发人员通常都是出于对技术的追求,更愿意采用先进的编程思想和理念,市场和业务的压力相对宽松,通常掌控开源项目的社区领袖都是技术方面的高手,能够正确地看待一些技术实践对质量的重要影响,所以整个开源项目的开发过程可能更加科学、合理和有序,尽管参与开源项目的人员分散在全世界的各个地方,却并不影响他们产出高质量的软件和成果。

Element3里的TDD

最近参与了开源项目 Element3 的组件开发,整个项目从立项之初,就确立了使用 TDD 测试驱动开发的原则和规范,因此参与的人员要么已经熟悉 TDD 的开发方式,要么是想学习和深入理解 TDD、Vue3 和 ElementUI 的学习者,因此在心态和意愿上具备了良好的基础,团队成员至少是不存在对于 TDD 开发方式的抵触和抗拒问题的。

在此过程中,我个人也进一步对 TDD 的开发方式体会更深了一些,下面聊一些我个人的感受:

其实很多时候,我并不想聊具体的技术,我觉得比技术更能改变自身的是心态,具体的技术资料,网上难道还不够多吗?现如今这个时代,怕是穷尽一生也学不完。说回到 TDD 这个开发方式,多数人都是有一知半解的,我自己可能多年以前就知道测试驱动开发是什么意思了,然而也就是近一两年才开始真正入门实践,为什么呢? 自己反思了一下,一个主要原因可能是缺乏见识,不知道这个 TDD 原来是这么好的开发方式,自己也没有深入思考过相关的问题,套用最近我看到的一个文体:

因为你没听到音乐,所以你会觉得跳舞的人疯了。

因为你没体会过 TDD 高质量代码的好处,所以你会觉得用测试驱动写代码的人疯了。

Progress组件

我个人目前只重构了一个组件: Progress,两个星期才重构完了一个 Progress 的组件,注意:仅仅是重构/重写,这期间的前提还是需求明确、实现方案已经现成的情况下,我再次重写,只是需要用测试驱动开发,保证代码质量,原来的具体实现方式已经是经过使用验证的,可以大量参考的,这中间减少了大量的沟通需求的时间,研究实现方案的时间等等,我猜如果要完全凭空实现的话,给我一个月时间还不知道能不能达到现在的效果。

“就这?”也许你会调侃一下我的开发效率了,我也自知是个能力一般、资质平庸的程序员,这很正常,但也很真实,我们最好还是承认我们自己是平凡的大多数,使用 TDD 后一个很大的好处是,即便是平庸如我这样的程序员,也可以写出拍着胸脯有自信的代码了,一种真正的脚踏实地的感觉,软件行业流行着一种观念是说:哪儿有软件没 Bug 的?有 Bug 很正常嘛!的确软件的复杂性,决定了软件出 Bug 的高机率,可是这样的观念和说法,却无行中助长了很多软件代码的粗制滥造,只求快速实现功能,不管未来的可维护性、可扩展性等等。

我这样的开发速度也许在一些“追求结果”、“唯快不破”的公司项目中是不能接受的,然而,这才是高质量软件的真实开发进度,前些天在极限编程群里有编程能力很强的朋友统计了自己一星期“无异味”(Sonar 扫描没有坏味道,即代码可读性很好)高质量的代码产出量,也不过是每天两三百行而已,放在一般的程序员能力来说,可能也就一天一百行左右吧,一百行代码能做什么呢?可能离很多公司领导心目中那种高速的期望非常远:“我这儿有个想法/需求,下周一能出个 Demo 吗?这个月最好能上线发布”。 这种速度倒也不是说不能实现,只是需要前期有相应的准备和积累,编程开发这件事,其实就是这样,积累得越雄厚,应变的能力才能越强,打造一支这样的开发团队,一种企业文化,一批批高素质的软件开发人才,自然能够做到“唯快不破”高效交付。

这就好像我们做的这个 Element3 组件库,或者其他的各种前端UI组件库,使用它的人可以非常快速高效地搭建出一个业务需要的界面,在一些中小公司手快的程序员一天做三个、五个增删改查的界面效果都是有可能的,然而公司领导或行外人士怎么会想到,其中的一个小组件居然要这么长时间开发?各种后端的开发框架和其他技术要素也是同样的道理,没有相应的积累,就没有高效的交付能力。

TDD的好处

下面结合这次开源组件重构说一些我所体会到的 TDD 的益处:

TDD 可以帮助我们理清需求和思路,因为上手要先写测试,那么要测试什么呢?这时就会先把最终要的结果分析清楚,然后拆分成可实现的小任务,从简入繁,先写出相应的单元测试,再推导出相应的代码实现,测试通过后,再写下一个单元测试,再倒推出业务代码实现,如此往复,这就是我们说的 TDD 的开发节奏。在这个重构 Element3 项目的 GitHub 仓库 Issues 列表中就可以看到参与项目的成员相应的任务拆解过程,一个组件的重构,都好几十个任务和子任务,整个过程冷静、耐心、严谨,一步一步,看着慢,其实很高效。 实际上越是复杂的组件,越是获益良多。

TDD 可以帮助我们更有信心地进行重构,真正的重构不只是在原来的代码上改就完了,《重构》这本书出了两版了,第一版是以 Java 语言为例写的,第二版是以 JavaScript 语言为例再版的,其中对重构有着明确的定义:

在不改变"软件之可察行为"前提下,调整其结构和代码。

关键字是如何“确保不改变可察行为”呢?根据很多编程大师多年的编程经验来看,只有100%全面覆盖的自动化单元测试才能确保。而要达到100%全面覆盖的自动化单元测试,最好的方法就是一开始就从写单元测试入手,测试驱动着写出相应的业务实现代码,这样整个过程下来自然而然就达到了100%的单元测试覆盖了,即使部分代码不需要测试,达到 90%以上也是非常轻松的事情,有了这样的保障,我们才能大胆地尝试新的想法,不抗拒变化,高质量高效率地持续交付。

按上面这个重构的定义来衡量,大多数我们平常项目中看到的直接修改实现代码的行为,在极限编程群里有个说法叫:“瞎搞”,体验过 TDD 的同学都明白,确实如此,回想起自己当年的“大刀阔斧”,不禁汗颜,确实是瞎搞。瞎搞带来的危害大家自然也能想到,频繁的 Bug 修复,不稳定的软件质量等,自然就会导致团队成员对任何的修改都心生抗拒,对于业务的需求和变化心生畏惧,对于源代码日渐陌生,即使非常小的改进也没有信心快速完成,每一次的更改和发布都需要大量的人力辅助测试、回归测试等,后续的成本耗费其实非常巨大、不可估算。

还有些时候不是需求变了,而是自己的想法变了,最初的实现可能考虑得不周到,可能采用的实现不理想,现在想用更好的方式改写,这时就需要有一个可以保障的安全网,这时 TDD 所构建出来的全面覆盖的单元测试又会发挥极大的价值,

还有就是发现 Bug 的时候,TDD 所构建的单元测试也可以帮助我们快速、准确地定位问题,如果现有的测试都可以正常通过,对照相应的测试和 Bug 产生可能的原因来分析,很快就能有解决的思路,通过补充针对相应 Bug 场景的单元测试,可能很快就能在本地快速重现出问题,相应地解决也将非常快速而准确。

就在这两三周的重构过程中,项目涉及了更改目录结构、转换到 TypeScript 语言等重大变化,这在一些公司里的项目可能是非常可怕的决策,然而因为有单元测试的保障,团队成员坚持使用 TDD 来进行开发,都非常有序而平滑地完成了这些变动。 在可预见的未来,有这样良好基础的项目,后劲会非常强大,如同飞轮效应,如同滚雪球效应,可以越来越高效地交付价值,满足用户的需求和变化。

最后,希望此文能引起你对 TDD 的一些兴趣和信心,实际体验一下,寻找一个采用 TDD 的团体,参与其中,真正跨过这个程序员的“分水岭”,是的,没错,在我现在的概念里,程序员可以分成两种,一种是会 TDD 的,一种是不会 TDD 的,这几乎成为了一道“分水岭”。不论语言、框架或者业务方向,都是如此,当你领略了 TDD 的威力后,你会发现,用这样的编程方式,各种编程语言可能都不再是界限,用这样的手法,一种类似工匠精神的手法,用什么语言编程都可以写出质量很高的软件代码,由慢到快,“天下武功,唯快不破” 就只是刻意练习的时间问题……