跳转至

结束语 对代码的信心要从测试里来

你好,我是郑晔!

《程序员的测试课》到这里已经接近尾声了,经过整个专栏的学习,相信你对“程序员该如何做测试”这件事已经有了一个更加完整的认识。这一讲我们不去深入技术细节,我想先从一本书和你聊起。

无知之错和无能之错

这本书叫《清单革命》,作者是阿图·葛文德,他是一名医生,曾是白宫最年轻的健康政策顾问。在书的开篇作者提到,人类的错误可以分为两大类型。第一类是“无知之错”,我们犯错是因为没有掌握相关知识。第二类是“无能之错”,我们犯错并非因为没有掌握相关知识,而是因为没有正确使用这些知识。无知之错,可以原谅,无能之错,不可原谅。

在作者看来,目前这个世界上还有很多疾病没有很好的治疗方案,这个算是无知之错。但在真实世界中,很多治疗的失败却是因为医疗团队没有做好该做的事,那就属于无能之错了。

这个分类方式给了我很大的震撼,让我一下子想明白很多事。有了分类,针对不同的错误,我们可以采用不同的修正方式。无知之错,因为欠缺的是知识,所以如果要修正这类错误,需要补充相关的知识。而无能之错并非知识的欠缺,所以,要修正这类错误,需要改进的是工作的方法和流程。

身为程序员,我们是幸运的,我们生活在一个对软件有巨大需求的时代。但实事求是地说,软件也是各种问题的高发地带,我们在其中不停犯着各种错误,很多甚至是低级错误。

这些错误应该归为哪种类型的错误呢?

在外人看来,软件开发团队就应该有做好软件的能力,没做好肯定就是各种疏忽造成,这种错误是一种无能之错,要解决无能之错,显然就应该从改进工作方法和流程入手。于是,我们看到很多公司一旦觉得自己的公司需要提升软件质量了,首先是引入一套新的流程,无论是拼命写文档,还是引入专人负责。然而结果是软件团队疲于应付各种流程,软件的实际质量却并未得到有效改善。虽然初衷是好的,但因为诊断错了病因,用错了药方,治不好病也就在所难免了。

软件质量的病不在外部,而在内部。一个没有质量意识的团队只靠外部的推动很难做出高质量软件,这就像一个孩子如果仅仅靠家长的逼迫很难取得长期的好成绩一样。然而,软件质量靠流程却成了行业中的常态,不得不说,这是一个悲哀的结果。孩子想取得好成绩,归根结底是要有自己对学习的热爱,同样,软件质量要想得到真正的提升,要将做到内建质量(Build Quality In)。

内建质量

内建质量,就是将质量的思考内建于软件开发的全生命周期中

说起来很简单,然而,产品经理一拍脑袋,程序员拼命加班,把一个漏洞百出的软件送到了测试人员手里。然后,在业务强大的压力之下,测试人员闭着眼睛,把一个质量不彰的软件送上了线。每个环节都在放水,结果就是水漫金山。线上系统问题不断,新的需求接踵而来,团队疲惫不堪。然而,没有人真正地想问题到底出在哪里。这才是很多团队的真实情况。

在这样的团队中,质量只是测试人员的事,相当于把整个团队的责任压在了少数人的头上,线上出问题就是情理之中的事了。内建质量就是要把软件开发中的每一个环节都加入质量的考虑:

  • 业务负责人不能只要求上线日期,也要给出需求验证的业务目标和业务的验收标准;
  • 产品经理不只是要给出产品说明,更要给出每个需求点的验收标准;
  • 程序员不只给出代码,还要给出覆盖每行代码的自动化测试。

所谓内建质量,本质上就是用任务分解的方式,让每个环节都交付满足一定质量标准的交付物。其实,现在软件行业已经懂得了用迭代交付替代瀑布式交付,把大的需求拆分为小需求的集合,逐步交付给市场,尽早收集反馈,避免走过多的弯路。而内建质量则是通过在每个环节中加入对质量的思考,在每一个环节都要验证交付物是否符合目标,尽早发现问题。这样才不至于让测试人员成为最后的防洪堤坝,才不至于把大招憋成内伤,才有可能拿出一个高质量的软件。

在软件研发的环节加入质量的思考,对很多人来说,是一件有难度的事。因为在他们看来,这么做是增加了工作量。比如很多程序员会说“写测试就是浪费时间”。然而,真的是这样吗?

一个内建质量的团队,可以在工作的诸多环节规避掉很多问题。从软件生命周期的角度看,规避了这些问题可以从整体上节省时间。虽然很多人学过软件工程的基本理论,但这种东西实在太反直觉,就像不相信 0.99… = 1,也有很多人不相信前期的投入会给团队带来长期的回报。

但这种不相信其实更多是一种不愿意相信,因为相信了就意味着要做出改变,而改变才是很多人真正惧怕的。是的,很多人真正的不愿意是“改变”这件事。正是因为有太多的人不愿意改变,才使得愿意改变的人很容易脱颖而出。

一旦你想明白了这一点,你就能理解软件研发中暴露的很多错误根本不是无能之错,而是无知之错。换言之,正是因为很多人只愿意墨守成规,所以,他们根本看不到自己其实是欠缺了一个质量的维度,而这个维度上也有着一张知识网。

把无知之错当做无能之错去解决,根本就是走错了方向,再多的流程改进也不会让人学会写测试。因为程序员欠缺的是写测试的知识,而很少有人会意识到原来很多程序员不会写测试。解决无知之错要从知识的补充入手,而前提条件是你愿意改变。

简单的代码

感谢你一路学到这里,我相信你是愿意改变的。现在你已经具备了改变所需的基础知识,相比于还在努力改变流程的人,你已经领先了很多。对你来说,接下来要做的是花更多的时间来练习,并在练习的过程中发现自己欠缺的知识,进行相应的补充。

《程序员的测试课》虽然给了你一个写好测试的知识结构,但估计你也发现了,其实写好测试要具备的知识储备并不小,就像我在形容 TDD 所说:

TDD 只是冰山一角,露在海面之上的是 TDD 的节奏,而藏在海面下的是任务分解、软件设计这些需要一定时间积累的能力。

同样,写出来的测试也是冰山一角,背后是那些需要时间积累的能力。但是,不要被这些东西吓到。其实,你在实战中已经见识过了,我写的代码很简单。有人会认为,这是一个演示的例子,所以写出来会很简单。但实际上,我在真实项目中也是这么写代码的,只不过业务逻辑更复杂一些。是的,业务逻辑复杂和代码复杂是两回事。不管业务逻辑多复杂,代码都可以写得清晰而简洁。只有那些写得不好的代码才是复杂的,会有着各种各样奇怪的写法。

优秀的代码平平无奇,糟糕的代码千奇百怪。

随着经验的丰富,我越能理解简单的价值,能坚持把代码写简单是一种能力。这需要我们不仅要有把代码写简单的意识,还要有把代码写简单的能力。好消息是,简单的代码也是容易写测试的代码,无论后续添加新功能还是修改已有的问题,难度都会下降很多,所以,它也是高质量的代码。

你只要不断地用测试作为一把尺子衡量你写的代码,你的代码质量就会越来越高。当你不知道该怎么办时,不妨回到专栏里,看看我们在专栏里是怎样解决问题的:把问题还原到简单的情形,再去想办法解决。化繁为简,是一个优秀程序员应该具备的品质。

如果整个专栏你只能记住一件事,那请记住:写代码时问问自己,这段代码应该怎么测。

在这篇结束语的末尾,我来讲个小八卦,跟你说说这个专栏是怎么来的。我在自己的公众号“郑大晔校”中写了一篇文章《为什么程序员大多不写测试?》,说的就是程序员不会写测试这件事。在文章的最后,我说程序员没有太好的方式学习写测试。《极客时间》的主编看到这篇文章就说,既然没有,那你来写一个吧。于是,有了这个专栏,也算是我自己挖坑自己填了。

这个专栏从开始构思到上线只用了一个月的时间,创下了我写作专栏的记录。算起来,这已经是我在《极客时间》写的第四个专栏了,我能够梳理成体系的结构化思考都以专栏的形式呈现了。当然,如果你还对我零散思考有兴趣,不妨关注我的公众号“郑大晔校”。

这次的《程序员的测试课》之旅就暂告一段落了!如果以后有机会,我会再来与你分享我对软件开发的理解。

最后是我们的小福利环节,我给你留了一个有奖小问卷,希望你花两分钟来填写一下,你的反馈意见对我来说很重要,我会根据你的意见持续维护这个专栏。

再见!

精选留言(15)
  • Gojustforfun 👍(20) 💬(3)

    我已为老师想好下一个专栏的名称《TDD项目实战》,很多人一提到TDD第一感觉就是听过、没见过。耗子叔早年写过《TDD并不是看上去的那么美》https://coolshell.cn/articles/3649.html,我对TDD还是很感兴趣的,一直自己摸索感觉还是不得其法。老师在极客时间的其实为TDD做了不少铺垫,是时候综合运用一下来一个中型的实践项目,把ThoughtWorks的TDD实战经验在实际项目中展示一下。 以下是我自我摸索出的想要玩转TDD需要的能力/技能: 1. 需求分解 《10X程序员工作法》 -- 解决测试用例从哪里来的问题? 2. 设计能力 《软件设计之美》 -- 设计/抽象/建模让产品代码更具可测试性,同时完成第一创造 3. 测试能力 《程序员的测试课》 --- 扬长避短,写出好的测试代码,别让测试成为负资产 4. 编程能力 --- 熟练掌握所用语言的核心语法、常用标准库、第三方库等(含数据结构与算法),能写出符合该语言规范的代码,判断标准:在TDD循环中尽量不卡壳、少用Google减少打断循环的次数。 5. 转换能力 -- TDD循环是在两个思维层次来回跳转,写测试时抽象需求What,写代码时具体细节How 6. 重构能力 《代码之丑》 -- 识别坏味道,运用恰当重构手法提高代码质量 7. 工具能力 -- 熟练操作Git、IDE、及其他自动化工具,使其能跟上TDD的节奏 (比如,编写测试时,产品代码还没实现但写出其接口后可以利用IDE快捷键自动生成产品代码的框架。重构时,运用IDE提供的重构快捷键,快速修改名称、抽取方法、内联函数等。完成几次TDD循环后可以用Git提交,设置“游戏存档”随时重新开始,如果一切顺利合并多个提交为一个然后提交) 最后就是各种场景实战,小型场景练习并熟练上述技能,中型场景强化上述技能的同时,培养在工期压力下完成任务的能力,大型场景就是在实际工作中实践。 我没把TDD当银弹,希望能达到“对于同一个业务功能,大多数人用X天完成产品代码然后提测,我同样用X天完成产品代码和测试代码然后提测”的标准就可以了。希望这种工作方式能给我带来更多自信(对代码也对自己)。谢谢!

    2021-09-28

  • 术子米德 👍(3) 💬(1)

    🤔☕️🤔☕️🤔 我个人写代码近20年 不肯写测试,第一原因是无知,以为写出代码就可以,第二原因是懒惰,因为无知和自大,发现既然配置了测试,那就毫无顾忌把测试抛出去,代价就是数年下来楞是把自己逼成一个修简单问题引起的难查问题的加班狗 最根本的原因,还是自己缺乏追求,对自己所从事的事业的热爱感 上述就是我自己的经历,在最近5年我越来越清楚自己的问题,也越来越努力去克服自己的问题,寻找意义感和热爱点,只有自己能点亮自性之光

    2021-09-17

  • Fredo 👍(2) 💬(1)

    郑老师的专栏文章语言很质朴,优秀的专栏读来酣畅淋漓,受益很多,接下来就该要自己多刻意练习了,加油!郑老师 再见👋🏻

    2021-09-17

  • 科富 👍(1) 💬(2)

    “不愿意相信”,说到重点了,相信就意味着要做出改变,对自己下手是需要很大勇气的。 在一个进度优先的氛围下,完成功能,成绩是显而易见的。软件质量如果没有一个量化的指标,做的多也未必能被看到个认可

    2021-11-22

  • asusual 👍(1) 💬(1)

    期待郑大新专栏哈哈哈哈

    2021-09-21

  • ifelse 👍(0) 💬(1)

    关注公众号又可以拉进与老师的距离了

    2022-06-15

  • 可怜大灰狼 👍(0) 💬(1)

    感谢老师,我个人觉得结束语比之前章节写的好,更深入人心。无知之错,无能之错,受教了

    2022-03-25

  • 许凯 👍(0) 💬(1)

    老师能出一个讲透权限设计的课程吗?

    2022-03-15

  • 树懒先生 👍(0) 💬(1)

    四门课程终于学完了。听着郑老师侃侃而谈,不胜感慨,TWer出来的程序员就是能说啊。从量,这个角度来看,这门课无疑是最少的;而从实际收获(有点唯心),又无疑是最满的。P.S. 一路跟下来的学生,能不能给点附加优惠,比如郑老师的联系方式啥的。

    2021-09-24

  • sylan215 👍(2) 💬(0)

    无知之错和无能之错这个分类太真实了,不过很多时候,我们没法分清自己是无知还是无能,比如老师举例说的程序员对于质量内建的理解。 内建质量的意义我理解大家都知道,但现实情况是,很多开发被进度逼着往前走,恨不得每写完一个函数就提测给测试,以表示自己「提测」了,根本顾不上自测(作为一个测试,还在给开发找借口,感觉有点别扭),久而久之,就懈怠了。 我也想过从测试侧通过流程,强制要求自测,不过这样就变成解决「无能之错」了,所以还是要让团队都意识到内建质量的重要性,特别是让项目负责人接受,通过解决「无知之错」来促成这件事。

    2021-09-17

  • aoe 👍(1) 💬(0)

    从此踏入编写单元测试的门槛。大多数程序员不写单元测试最重要的原因是:1、不影响自己收入;2、没看到影响公司收入。

    2021-11-13

  • 北风一叶 👍(1) 💬(0)

    专栏看完了,之后就是看完了,然后就没有然后了

    2021-09-25

  • 6点无痛早起学习的和尚 👍(0) 💬(0)

    2023年05月02日08:13:23 读完了是第 703 位,这是一个可以立马实践的专栏,最近正好有个新项目,先实践起来,实践过程中遇到问题再回过头再来看这个内容!!循环循环再循环

    2023-05-02

  • Frode 👍(0) 💬(0)

    cool,一口气看完!!最近公司补单元测试和TDD,从20%补到80%,再次看郑大文章收获颇丰啊!!目标向着100%前进。

    2022-11-19

  • 尔霍 👍(0) 💬(0)

    好的理由很简单,乱的系统千奇百怪

    2022-07-28