A song of TDD and BUG - 前奏曲

TDD 与 BUG 的爱恨情仇(卷一)

前言

这是最好的时代,TDD 的出现终结了 BUG 横行的蛮荒,从此 Coding 世界进入了秩序与光明;这也是最坏的时代,历史的包袱,无尽的重构,博弈与妥协充斥在每个角落。从电脑和编程出现之初 Bug 就如影随形,那只飞到 Mark II 上的 Bug 穿越了大半个世纪,仍是程序员们在梦中惊醒,在镜前流泪的主要原因。那 Bug 为什么会出现?有没可能消除 Bug? 这是个哲学问题。

Bug的相对性原理

有人说 Bug 是一个 Human Error,是可以通过一定的约定,准则和谨慎去避免的。的确, Bug 的多少很大程度上是程序员的谨慎程度来决定的。但是,我先把我的结论放在这, Bug 是无法避免的,也就是说他是一个在排除了人为错误之外的系统性的存在。

这听起来很像给自己开脱(其实就是给自己开脱),但是我是有理(狡)论(辨)支持的。比如之前有人提出来过抽象漏洞定律来解释这个问题,但是这个太笼统,听起来也太抽象,我想用自己的理解来解释一下。首先我们需要定义 Bug 是什么,维基百科中 Bug 的定义是程序错误。我们在这给它一个更明确,更有意义的定义,就是: Bug 是程序中与预期情况表现不符合的逻辑。比如:产品让我点击购买键下单,我写成了点击购买键弹窗嘲讽用户买不起。这就是一个 Bug。从上边的例子里我们可以发现,之前定义中的预期是需要参照物的,而这个参照物是什么,这才是真正的 tricky part。如果这个参照物值得是所有人的预期表现,那这个问题就好回答了,每个人或者说至少有一些的对不同同一个功能有不同的预期,这样的话在一些人看来正常的代码,在另一些人看来就是 Bug,这就是 Bug 的相对性。而如果这个参照物是指的产品需求,这可能就需要一番解释了。

在这里我想引入两个概念:需求的时间局部绝对正确性和需求的绝对进化性。听起来很 fancy,其实很简单(你不小心发现了程序员术语的真相)。需求的时间局部绝对正确性是指一个需求在下一次需求到来之前是具有绝对的权威性的,需求的绝对进化性是指两个相邻需求之间是至少有一处不同的。了解了这两个概念,后边的推理就简单多了。使用数学归纳法,假设上一个代码和需求是完全符合的,在下一个需求到来的时候,根据需求的时间局部绝对正确性,这时候具有绝对正确性的需求是新来的需求,而又由于需求的绝对进化性,新需求和上一个需求之间至少有一处不同,也就是现在的代码至少有一处跟新需求不同,根据 Bug 的定义,也就是至少有一个 Bug。有人会说,那假设我不变需求不就是不会有这种 Bug 了吗,可别忘了,从无到有也是一种情况。这个本来就是一个很有争议的问题,有什么想法也可以在评论区一起讨论。

程序员的千层饼理论

听了我瞎扯了这么久,你可能在想这人到底想说个啥。我其实想表达的是 Bug 如风,常伴吾身,我们要学会去与之共存。但是与之同存并不是对其容忍(skr),我们的先辈们披荆斩棘为我们探索出了很多路,其中一条非常亮眼的就叫单元测试。作为程序员对单测有一定了解或者至少有所耳闻,但大部分对自己对于单测的认知多多少少都有一些障碍。在这里我就想借鉴一下 LOL 直播界的哲学家大司马老师的千层饼理论来阐述我的观点。对于单测,很多程序员都认为自己看到了第五层,觉得自己在第二层,其实自己是在第一层,像我这种马虎点的甚至都在地下一层。那这里边的第几层分别代表什么呢。第五层代表不写任何种类的测试就能做到杜绝人为的 Bug,第二层代表会写单测就是懒得写而已,第一层代表并不会写单测,而地下一层代表不但不会写单测而且只会写 Bug。你品,你细品,自己属于第几层。

你不愿写单测的原因

不愿写单测的原因有千千万,但是大体可以分为以下三类:

  1. 业务都写不完,哪来的时间弄这破玩意
  2. 已经有测试了,没有必要浪费时间去写单测,我还要追求诗和远方
  3. 业务逻辑太简单了,我就是人形单测

单测的重要性

其实我之前跟其他人一样也会有很多的顾虑(其实是懒),之前看一些教程如果碰到关于单测的部分我都是跳过或者边看视频边“学”完,但是在工(被)作(迫)需(无)要(奈)之下我开始接触关于单测和 TDD 的东西,现在可以说是单测最忠实的追随者了。现在我就替你来一一打消上边提到的所有顾虑。

  1. 关于第一点顾虑:没有时间。只要你跟着这个系列教程,会让你对单测有更深的了解,至少会带你入门并且写出第一个单测,你就想像我这么懒的人都能学会,你凭什么学不会。
  2. 关于第二点顾虑,我主要想分成两部分来分析 - “有测试” 和 “浪费时间”:
    • 有测试:团队内的 QA 所做的测试都是黑盒测试,也就是他们只能告诉你现象,更好一点告诉你复现的具体步骤,有经验的测试可能会告诉你他们的猜测。但是猜测毕竟只是猜测,你还是需要一步一步去调试,去寻找 Bug 的根源。但是如果是单测跑失败了,你只需要看一眼单测的名字,就可以基本上定位到问题所在了。
    • 浪费时间:上一点已经有一点涉及到节省时间了,但是这还不够。我们做一个项目,一般都不是只做一版不需要迭代,也免不了重构。只要是需要迭代或者重构,就有单测发挥优势的机会,因为在这些过程中所产生的副作用,比如对之前逻辑的影响,都会由之前的单测来保证,这会很大程度上节省我们排查旧逻辑的时间。
  3. 关于第三点顾虑,业务逻辑简单是相对的,对于你来说,旧逻辑都是你写的或者是你维护的,但是对于新来的同事,他们可能就会一头雾水,需要一段时间去了解业务,但是如果有单测(BDD 更好,这里挖个坑,以后填),通过看单测,他/她就会对业务有个大概了解,对于之后的工作有很大的帮助。

什么是 TDD

再声明一遍,不是 PDD,不是 SSD,更不是 PTSD。下边我们有请开发模式界最耀眼的新星,不健壮代码的救世主,莫得感情的 Bug 收割机器 - T·听起来就很炫酷·DD。TDD 的全称是 Test-Driven Development,翻译过来就是测试驱动开发。它是敏捷开发的一项实践和技术,它背后的行为哲学就是以测试为导向进行项目的开发。

它有四个步骤:

  1. 写一个失败的单元测试
  2. 完善代码让测试通过
  3. 重构
  4. 重复上述步骤到天荒地老

看起来貌似很简单的样子,但其实里边另有乾坤,后边会进行进一步的解释。

赛博朋克演示法

本系列我会继续使用我最喜欢的赛博朋克演示法,那有同学就要问了,什么是赛博朋克演示法呢?其实大家都在写博客或者做分享的时候用到过,只不过这个方法被我冠名成赛博朋克演示法,我还没有看到其他人这么叫过,所以算原创吧(小得意一下)。

众所周知,所谓赛博朋克就是高科技,低生活。那套用在这里就是用一个特别傻的项目去演示一个特别 fancy 的概念。所以贯穿整个系列我都会用一个天气的项目叫做 Rainstorm 来展示单测和 TDD 的概念,这个项目不是我的原创,是我在学习 MVVM 的时候跟着 cococast.com 一起做的,这个不是广告,只是既然用了人家的东西,总得给人家一个名分,嗯嗯。

如果你对我说的这些都感兴趣,那就跟着我的节奏一起摇摆(不好意思,走错片场了),跟着我的步伐一起深入了解 TDD,欢迎观看下一集 A song of TDD and BUG - 主歌

本文结束 感谢您的阅读

本文标题:A song of TDD and BUG - 前奏曲

文章作者:Penstein

原始链接:http://cyberhex.me/2020/06/14/tdd-part-one/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%