在软件开发行业里,测试是程序员之间的一道分水岭,一边是大量高水平程序员不断在说测试的重要性,另一边是行业中大量的程序员不写测试。
一个“英雄”故事十几年前,我在《程序员》杂志上看到一篇文章,介绍了一个程序员创业的故事。他辛辛苦苦写了一个软件,解决了一个重要的问题,指望着凭借这个软件能够一鸣惊人。每次他给客户做演示,效果都很好,然后,有客户会提出来要亲手试一下,但只要客户一上手,各种各样的问题就来了,程序不是崩溃,就是告警,他只好顶着压力在现场去解决问题。
当年看这篇文章时,我看到的是一个程序英雄,而今天回过头来看这个故事,我看到的是一个不太懂测试的程序员。
之所以他的演示一切顺利,因为演示是按照他预先设定好的路径在进行,在软件开发中,我们称这样的路径为HappyPath。如果一个软件的HappyPath都走不通,那显然是无法出厂的。但魔*都在细节中,用户上手,他们永远不会按照你设定的路径来走的,在程序员眼中,这些都是异常路径。一旦有异常场景被触发,各种问题也就随之而来了。
这时候,你能怎么办?抱怨用户不懂你吗?也许你更应该抱怨自己的不专业。
只考虑HappyPath,不考虑异常场景,这是很多程序员写程序时最容易出现的问题,因为HappyPath的魅力实在太过耀眼,我们的几乎全部注意力都会被它带走,但是,决定一个程序质量的往往是那些异常场景,而对异常场景的考量程度,也决定着一个程序员的职业程度。
你不妨回想一下自己最近的几个Bug,有多少是打死我都想不到的,有多少是考虑不周造成的。每个程序员都会有“想大嘴巴子抽自己”的Bug,这么简单的事,我当时咋就没注意到呢,这些问题只要在开发阶段稍微多考虑一下,就不至于后来浪费那么的时间。你问我怎么知道?唉,全是血泪教训,
程序员要怎么逃脱只考虑HappyPath的初级阶段,向更高级迈进呢?他需要有测试的视角。
不断扩展的程序员“标配”写软件应该有测试,这是毫无疑问的。因为测试是与质量相关的,每个人都希望自己的软件是高质量的,高质量的软件需要尽可能把方方面面的问题都考虑到,而这恰恰是测试视角看到的东西。
但如果我问“程序员该不该写测试”,这时候答案就会出现分歧,有的人认为应该,有的人则嗤之以鼻。
让我换个问题,程序员应该懂设计模式吗?大部分程序员都会说应该,很多人会说,这难道不是程序员的基本功吗?但你要知道,只要把时间往回拨,在21世纪初,程序员不懂设计模式才是常态,很多人会嘲笑设计模式让代码变得复杂了。
再换一个问题,程序员应该懂面向对象吗?大部分程序员都会说应该,很多人会说,这难道不是程序员的基本功吗?但你要知道,只要把时间往回拨,在上个世纪八九十年代,结构化编程才是常态,很多人会嘲笑面向对象的复杂语法,还有运行时的负担。
好吧,类似的问题其实还有,我们先打住。看了上面的问题你就会发现,我们今天习以为常的很多东西,并不都是那么自然而然的,它们也是经过一段时间的发展才成为程序员的“标配”。
我们返回头来想一下,为什么面向对象和设计模式成了今天程序员的标配?因为软件开发正在变得越来越复杂,导致的结果就是,程序员的“标配”正在逐步地发生改变。
我曾经看到过一个段子,90年代的程序员使用PowerBuilder,一个人就可以写一个软件,二十一世纪初,程序员使用JSP,十个人写一个软件,而到了今天,程序员开始搞微服务了,一个软件要几十个人来写。这个段子固然有调侃的成分在里面,但这里面的重点是,软件的复杂度早已今非昔比。
复杂度带来的问题是什么呢?且不说理解业务和软件设计的难度大幅度提升,从程序员切身考虑,最大的问题是,一旦出了问题,定位的难度是急剧上升的。试想一下,如果一个微服务的系统中出了Bug,你得先定位到是哪个服务出了问题,然后,再到服务里面去定位具体的问题,作为一个有经验的程序员,我们知道,这其中的每个步骤都不像说起来那么轻松。
之所以程序员的“标配”要不断扩展,扩展进来的东西,都是为了对抗日益复杂的软件复杂度,目标都是为了让自己日后的工作更好做。面向对象也好,设计模式也罢,它们的出发点都是尽可能降低因为需求带来的工作量急剧提升。
在今天,测试,尤其是自动化测试,也应该成为程序员的“标配”,因为它解决了同样的问题。随着软件复杂度的提升,在一个系统中定位问题的难度越来越大,当我们花大力气好容易定位到问题,正如我们前面所说,你会发现,很多问题又不是什么高级问题,相当大的一部分是一些本来可以在开发过程中解决的问题,而这恰恰是自动化测试能够解决的问题。
心有余而力不足说到这里,你会发现,测试视角可以帮助程序员看到很多问题,自动化测试和程序员“标配”扩展的趋势也是一致的,而且你只要稍微多了解一些,你就会发现,支持写测试的各种工具框架层出不穷,写测试的方法也翻着各种花样:TDD、BDD等等,还有像Google这样大公司对测试要求也是极其严格的。
然而,当你心里对测试有了一丝认同之后,你又会冒出一个疑问,应该成为“标配”的自动化测试为啥至今没有成为程序员的“标配”呢?可能你想不到,这么严肃的问题居然有个很可笑的答案:非不为也,实不能也。没错,大部分程序员不写测试的原因是,不会写测试。
测试有什么难的吗?不就是用xUnit之类的单元测试框架写代码吗?程序员每天都在写代码,写代码的事能难倒程序员,这不是开玩笑吗?
如果你把这个问题抛给一个想写测试的程序员,他会告诉你,为了学着写测试,他了解过xUnit的框架,也看过人家演示TDD如何去做。别人做起来,他觉得写测试是那么容易。
可当他有了跃跃欲试的冲动,看到了自己的代码库,所有的兴奋都烟消云散了。他还是不知道怎么写出一个测试,可能是他的代码库太复杂了,他不知道该从哪里下手,也可能是跟着别人写测试很容易,到自己写测试的时候,他不知道第一个测试该从哪里开始。
有很多反对自动化测试的程序员,他会给你很多他认为的自动化测试不重要的理由。但如果有机会和他深入地聊进去,你会发现,本质的答案是,他不会写测试。如果你非要问他测试如何写,他只能给你一些很宏观的角度,比如,从接口上去测试、按照需求去测试云云,你会发现,这些原则上正确无误的说法,其实并不能很好地指导你的工作。讨论那么多,能力不足是原罪。
小结怎么样才能学会写测试呢?最好的办法是跟着会写测试的人一起写一段时间,但整体行业的环境决定能提供这样机会的公司少之又少。大部分人学习测试,还是要通过阅读书籍。所以,经常有人让我推荐关于测试的书,遗憾的是,我确实没什么可以推荐的。
关于测试的书,要么是告诉你一些框架工具怎么用,这种东西通常看文档就能解决,要么是讲实践,比如TDD,但还是那个问题,作者解决问题很爽,但和你有什么关系呢?归根结底,缺少一根主线,把所有这些东西连起来,让测试的知识成为一个整体。
其实,关于这个问题,如果需要一个宏观的答案,我在《10x程序员工作法》已经给出了:如何下手写测试,要学会任务分解,而很多测试不好做,本质上是软件设计有问题。
程序员,你想学会写测试吗?
郑晔鼓励鼓励我!