“单元测试要做多细?”

“单元测试要做多细?”

这篇文章主要来源是StackOverflow上的一个回答——“How deep are your unit tests?”。一个有13.8K的分的人(John Nolan)问了个关于TDD的问题,这个问题并不新鲜,最亮的是这个问题的Best Answer,这个问题是——

“TDD需要花时间写测试,而我们一般多少会写一些代码,而第一个测试是测试我的构造函数有没有把这个类的变量都设置对了,这会不会太过分了?那么,我们写单元测试的这个单元的粒度到底是什么样的?并且,是不是我们的测试测试得多了点?”

答案

StackOverflow上,这个问题的答案是这样的——

“I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence (I suspect this level of confidence is high compared to industry standards, but that could just be hubris). If I don’t typically make a kind of mistake (like setting the wrong variables in a constructor), I don’t test for it. I do tend to make sense of test errors, so I’m extra careful when I have logic with complicated conditionals. When coding on a team, I modify my strategy to carefully test code that we, collectively, tend to get wrong.”

老板为我的代码付报酬,而不是测试,所以,我对此的价值观是——测试越少越好,少到你对你的代码质量达到了某种自信(我觉得这种的自信标准应该要高于业内的标准,当然,这种自信也可能是种自大)。如果我的编码生涯中不会犯这种典型的错误(如:在构造函数中设了个错误的值),那我就不会测试它。我倾向于去对那些有意义的错误做测试,所以,我对一些比较复杂的条件逻辑会异常地小心。当在一个团队中,我会非常小心的测试那些会让团队容易出错的代码。

这个回答对TDD似乎有一种否定,最亮的是这个问题是由Kent Beck,Kent是XP和TDD的创造者,是敏捷开发实践方法的奠基人。以致于还有人调侃到——

The world does not think that Kent Beck would say this! There are legions of developers dutifully pursuing 100% coverage because they think it is what Kent Beck would do! I have told many that you said, in your XP book, that you don’t always adhere to Test First religiously. But I’m surprised too.

只是要地球人都不会觉得Kent Beck会这么说啊!我们有大堆程序员在忠实的追求着100%的代码测试覆盖率,因为这些程序员觉得Kent Beck也会这么干!我告诉过很多人,你在你的XP的书里说过,你并不总是支持“宗教信仰式的Test First”,但是今天Kent这么说,我还是很惊讶!

后面还有一些人不同意Kent, 我一下子从这个事中想到了《fight club》里的那个精神分裂者创建了一个连自己都反对的地下组织。呵呵。

其实我是非常同意Kent的,怎么合适怎么搞,爱怎么测试就怎么测试,只要自己和团队有信心就可以了。没有必要就一定要写测试,一定要测试先行。

其它答案

八卦完了,我们还是来认认真真地看看这个问题中其它的其它答案,因为这个问题的也是国人爱问题的问题。

第二个答案:值得借鉴

  • 开发过程中,单元测试应该来测试那些可能会出错的地方,或是那些边界情况。
  • 维护过程中,单元测试应该跟着我们的bug report来走,每一个bug都应该有个UT。于是程序员就会对自己的代码变更有两个自信,一是bug 被 fixed,二是相同的bug不会再次出现。

第三个答案:给敏捷咨师看的答案

这个答案在说,我们只注意到了TDD中的T,而忽略了第一个D,就是Driven…… bla bla bla… 又这扯这些空洞的东西了,国内的各种不学无术的敏捷咨询师最好这一口了。

第四个答案:致那些什么都要测试的人

如果我们需要测试一个像 int square(int x) 这样的开根函数,我们需要40亿个测试(每个数都要测试)。

事实上这种情况可能还更糟糕,如果有这样一个方法 void setX(int newX) 不会更改其它的成员变量,如:obj.z, Obj.y,那么,你是不是还要去测试一下别的变量没有被改变?

我们只可能测试那些有意义的,确实要测试的案例。

我的观点

我在《TDD并没有看上去的那么美》一文中说过我的观点了,我就不再多说了。我还是把下面这些观点列出来,供大家思考和讨论:

1)我国的教育对我们最大的洗脑不是掩盖事实,而让我们习惯于标准答案,习惯于教条,从而不会思考!敏捷开发中的若干东西似乎都成了软件开发中对某种标准答案的教条,实在是悲哀!

2)软件开发是一种脑力劳动,是一种知识密集型的工作,就像艺术作品一样,创作过程和成品是没有标准答案的。

3)软件的质量不是测试出来的,而是设计和维护出来的。就像工匠们在一点一点地雕琢他们的作品一样。

UT的粒度是多少,这个不重要,重要的是你会不会自己思考你的软件应该怎么做,怎么测试。

(全文完)

(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)

好烂啊有点差凑合看看还不错很精彩 (30 人打了分,平均分: 3.90 )
Loading...

“单元测试要做多细?”》的相关评论

  1. 沙发?测试驱动,像其他很多东西一样,在中国人的手里就变味了,支持博主,很高兴欣赏到有思想、有深度的文章

  2. 这个,我一看到关于测试的题目就激动,就想回复,难以抑制的哦。
    TDD什么的我不懂,但是说到单元测试,我还懂一点儿,
    我想说,我们的程序员要是把教科书上关于单元测试的部分都做好了,那么就OK 了。
    即使是作个人项目,也要这样做啊。
    当然,我们都希望编码时就一次性地完成到最佳质量,这是我们的追求目标。而一个编码人员的代码质量水平不是一开始就很高的,必然通过单元测试得到逐步的提高。所谓对代码的自信,也是通过积累了丰富的单元测试经验得到的。也就是高水平的编码人员的低BUG率的取得,是因为在编码的时候,就考虑到了可能发生的问题,并加以避免吧。从而进一步减低了他在单元测试中发现BUG的几率。

    我同意作者说的,软件质量不是靠测试达到的,而是靠设计的,没有好的设计,就没有所谓高质量的代码,那么即使是自己作测试,也是很痛苦的事情。

    至于覆盖率的事情,即使编码人员不做到100%覆盖率的话,QA也会指出,就算达成100%覆盖率也不能保证没有BUG吧。所以100%覆盖似乎是测试要达到的最低目标呢。(单纯的语句覆盖,并不能保证代码达到质量要求)

  3. 对于要做多细,我的经验就是:当我不在的时候,如果同事打电话说有Bug,询问是否会是我的问题,而我可以很自信的回答No的时候,就可以啦! 所以最享受的就是大家加班,而我在放心玩乐的时刻! :)

    这让我想起了一件事情,有位同行朋友学车,一开始总想让师傅告诉他,需要“打点方向”的时候到底是多少度? 需要“加点油”的时候到底是加到每小时多少公里? 后来知道了,没人会告诉你,你得自己Do!

  4. 皓哥说得很有道理,尽信书不如无书,太固守标准反而丧失了人的主观思考的能动性,书是死的,人是活的,懂得去思考,比固守标准要来得实际。

  5. 其实我觉得 在现今大型企业的团队中, 开发人员的职责就是开发和保证代码正确的实现 至于测试则基本是交给测试的人员来做 而测试人员又拿着测试的工资 他们不会考虑开发人员的水平 而是循规蹈矩的去完成整个测试流程@laoyin

  6. 3)软件的质量不是测试出来的,而是设计和维护出来的。就像工匠们在一点一点地雕琢他们的作品一样。

    sb 这句话充分暴露了你对TDD的理解是DDT, 开发驱动测试, UT里面的test,不是去测试你开发代码的bug。

    TDD中的,test,本身就是这个雕琢的过程,先测试后开发, 测试的是你的思路,帮助你设计合理的代码, 而不是DDT中的,开发驱动测试,事后去测试bug。

  7. 引用前面yum:“这让我想起了一件事情,有位同行朋友学车,一开始总想让师傅告诉他,需要“打点方向”的时候到底是多少度? 需要“加点油”的时候到底是加到每小时多少公里? 后来知道了,没人会告诉你,你得自己Do!”

    这个具体的例子我深有体会。哪个人不是从新手成长起来的?驾车师傅如果不能在最开始给出具体的可以量化的指导意见(类似CookBook),新手就很难入门更别提总结经验了。说白了就是教授方法不当,我当年也深受其害。像十字路口拐弯这么常见的情形,直接说打一圈不就行了吗;结果你告诉我“凭感觉”问题是我一个新手哪来的感觉?像软件开发中的设计模式、TDD、围棋中的定式,其实就是前辈的优秀经验总结,它们未必是最佳的,但也一定不是最差的,绝大多数情况下都是“不错”的方案。新手最欠缺的恰恰是这样可以直接采纳的指导方案,等你运用成熟了再考虑发展/创新也不迟。而排斥这些方案的,多是介于新手(只懂基本操作)和高手(会发展创新)之间的半瓶子水。

  8. 关于StackOverflow上出现的大量排斥TDD的观点,我尝试这样来解释:占主流群体的底层程序员在没有监督的情况下缺乏责任心只求应付工作。这是很普遍的现象,在学校里,老师来了人模狗样的写作业,老师走了立马乱成一锅粥各玩各的。相信大多数人都经历过,其实事后想想老师的监督还是为我们好,可是当时的学生就是反感老师管。到了工作岗位上也是如此,领导在不在大不一样。总结下来就是,大多数人在没有监督的情况下容易慵懒应付。这应该是人的天性。后来工厂出现的流水线,应该就是对付这种情况的。把工作流程标准化、流水线化,让整个流水线上的人都没机会偷懒。我想这也是TDD的目标之一。如果没有足够的测试用例,你怎么确认某个模块是功能完整的无明显缺陷的?如果原作者离职了,后来的维护者修改了先前的代码,又如何保证不影响以前的功能?要知道,你修改一个新bug,很可能会把以前的多个旧bug放出来,有经验的开发者应该明白我说的什么意思。于是软件越来越难于维护,维护者也越来越排斥修改代码,一潭死水无人敢碰,软件提前寿终正寝。我最终的结论就是,TDD要由项目管理者推动实施,而不能征求下面程序员的意见,——他们虽然人数众多,但就像面对老师的学生一样,总是要求玩耍而不是学习,这对项目本身是无益的。

  9. lixinqi :
    还是啥都没说

    是啊,对于“单元测试要做多细?”这个问题,楼主还是没有给出具体的、可量化的、可操作的答案。说来说去还是“自己看着办”。这就好比驾驶教练回答学生“转弯时要打几圈方向盘”时给出的答案“凭感觉”,没有任何指导性可言。人家都说带着问题来的,结果还得带着问题走。

  10. 其实我很明白驾驶教练的苦衷,因为每个车型都不一样,无论他怎么回答,总能被证实是错的。于是他只能选择非常笼统的永远正确的模糊答案。但是这种答案对学生毫无益处。

  11. 哈哈,很明显是我, 不过我已经和你道歉了 , 微博删除了。 我对事不对人, 观点不一致,一时的冲动, 不要往心里去了哈。 @陈皓

  12. 赞成,项目中的质量控制方面,需要有PM或者Leader承担指导的责任,具体的做法可以通过编制编码规范,内部指导准则,以及PILOT加Code Review,在项目初期把代码质量稳定住。同时,使得编码人员获得相应的经验。@liigo

  13. laoyin :
    赞成,项目中的质量控制方面,需要有PM或者Leader承担指导的责任,具体的做法可以通过编制编码规范,内部指导准则,以及PILOT加Code Review,在项目初期把代码质量稳定住。同时,使得编码人员获得相应的经验。@liigo

    跑题儿了。

  14. 皓哥,题目挺好,说的也对,就是后面说着说着把UT和TDD混为一谈了,确实有些乱。UT更关注单个的test case,目的是保证接口中每个分支的逻辑正确性;TDD更重要的是中间那个D,而目的是后面那个D。

  15. 我觉得主要还是写代码的人有一份责任心,就什么就齐了。
    条条框框是用来约束没有责任心的人的。

  16. 说白了还是看你怎么用,TDD也只不过是个方法罢了,工具而已,又不是思想或者本质之类的。我个人开发就很不TDD,即使需要写测试也是针对公开的接口或者某些特定的调用去做测试程序,这样的好处是达到测试代码的目的,同时又有了一个测试程序。缺点是这个测试程序不带好集成到发布过程。

    但换言之,好的程序员写的代码自然质量较高,差的程序员有没有单元测试一样差,甚至于无法保证差的程序员写的测试单元没有BUG,如果有BUG那不是更悲剧?

    楼主的文章看多了也有些感触,就是作者个人的观点多些,而且有些偏激。

  17. 我遇到的很多敏捷团队,把单元测试的覆盖率当成是一个指标了。。。实际上软件质量并没有明显提高,反而是下面一帮开发的兄弟累死累活的。。。

  18. “like setting the wrong variables in a constructor” 中的variables翻译错了,是“变量”的意思

  19. 我觉得某大牛木匠造桌子的比喻很好:外行怎么看造桌子呢?一块木板钉四条腿。

    但这样的桌子能用吗?碰碰试试,歪了;放俩碟子,倒了;搁个盆子,散了。

    那么怎么把桌子做好呢?

    一种方式就是TDD:用各种方法做桌子,用合理大小的力来碰之、压之、推之……直到它不再倒为止。

    另一种方式呢,跟老师傅学,他会告诉你:别把桌腿搞成个平头用钉子硬钉,吃不住劲;用aa木头的话,一般承重xx,桌腿至少yy寸粗细;接榫有N种形式,它们的特性分别是……

    做过一定时间后,无论哪种方式,都可以获得丰富经验,然后就可以根据经验直接说“A方案行不通;B方案的承压上限大约是……;C方案虽足够结实,但成本远远超出预算……”

    回到软件上,它和木匠不太一样,因为你总是在做此前没有做过的东西;但这并不代表你之前的经验就没用了——比如说,做了一辈子桌子的你,第一次去搭个戏台子:显然它的支撑腿和桌子腿没有根本区别,只是放大了的“桌子面”可能带来更大的变形,并导致“桌子腿”侧向受力——而以上就是第一次搭戏台子的你需要重点关注的地方。

    台面变形无可避免;但我可以不把它和台柱做死,而是通过铁链之类绞合。这样台柱就不会变形并因此受侧向力了;台面断裂则可以通过实验,找到合适厚度;台柱也要按以往经验放大以保证有足够支撑力……

    解决了以上几点,其他地方显然就不必测试了。

    事实上,以上其实就等同于“老板为我的代码付报酬,而不是测试,所以,我对此的价值观是——测试越少越好,少到你对你的代码质量达到了某种自信(我觉得这种的自信标准应该要高于业内的标准,当然,这种自信也可能是种自大)。如果我的编码生涯中不会犯这种典型的错误(如:在构造函数中设了个错误的值),那我就不会测试它。”

    所以,显然,对一个不了解的领域,测试——或者说实验——显然可以帮助开发者迅速了解业务;但测试/实验显然无法带来解决方案,解决方案只能是人想出来的,不可能通过某些固定的步骤得到(比如说,前面把戏台子的台面和台柱分离绞合的方案,显然不可能通过“用1280种方式折腾桌子”得到)。

    在这点上,某些TDD的信徒显然犯了和前些年的面向对象教、UML教一样的错误。

  20. “这个回答对TDD似乎有一种否定,最亮的是这个问题是由Kent Beck,Kent是XP和TDD的创造者,是敏捷开发实践方法的奠基人。” 这句话中的问题应该改成回答吧?

  21. @usb
    怎么能骂人呢,现在行业开会就是各种模式各种法各种犀利,实际工作就各种不顺各种套不上各种员工老板不给力。行业闭塞了.

  22. liigo :
    关于StackOverflow上出现的大量排斥TDD的观点,我尝试这样来解释:占主流群体的底层程序员在没有监督的情况下缺乏责任心只求应付工作。这是很普遍的现象,在学校里,老师来了人模狗样的写作业,老师走了立马乱成一锅粥各玩各的。相信大多数人都经历过,其实事后想想老师的监督还是为我们好,可是当时的学生就是反感老师管。到了工作岗位上也是如此,领导在不在大不一样。总结下来就是,大多数人在没有监督的情况下容易慵懒应付。这应该是人的天性。后来工厂出现的流水线,应该就是对付这种情况的。把工作流程标准化、流水线化,让整个流水线上的人都没机会偷懒。我想这也是TDD的目标之一。如果没有足够的测试用例,你怎么确认某个模块是功能完整的无明显缺陷的?如果原作者离职了,后来的维护者修改了先前的代码,又如何保证不影响以前的功能?要知道,你修改一个新bug,很可能会把以前的多个旧bug放出来,有经验的开发者应该明白我说的什么意思。于是软件越来越难于维护,维护者也越来越排斥修改代码,一潭死水无人敢碰,软件提前寿终正寝。我最终的结论就是,TDD要由项目管理者推动实施,而不能征求下面程序员的意见,——他们虽然人数众多,但就像面对老师的学生一样,总是要求玩耍而不是学习,这对项目本身是无益的。

    这是典型的流水线工人的管理模式,程序员从事的不是计件工作,用加班时间 测试覆盖率来衡量程序员的工作都是错误的。程序员是有自尊心的,好的程序员,不需要用加班 用所谓的UT 测试覆盖来证明自己,他自己就会要求自己做到极致。差的没有责任心,没有上进心的程序员,做了这些也没用。

    这种管理模式,就是不分良莠,一律把程序员当成贱骨头,除了打击程序员的积极性,降低程序员的效率意外没有其他的好处。最终,程序员都成了唯唯诺诺,没有什么创造力,一个模子刻出来的。

    所有的管理,都是扯淡,软件公司,人是最重要的,人开心了,效率就会提升,你把自己的程序员像防贼一样的防着,很难想象程序员会快乐,会高效,对公司会有认同感

  23. hewig :
    “这个回答对TDD似乎有一种否定,最亮的是这个问题是由Kent Beck,Kent是XP和TDD的创造者,是敏捷开发实践方法的奠基人。” 这句话中的问题应该改成回答吧?

    既然看明白了,何必字斟句酌。

  24. 不同类型的公司,有不同的文化,这是肯定的。
    对于需要投入大量人力进行开发的公司,由于每个人的能力水平不同,必须要把过程标准化,才能获得可控的质量。换句话说,可能个体的平均水平不会很高,但是做出的产品是有质量保证的。这时的效率,完全基于合理的开发过程流水线的设计。

    对于小团队来说,更重要的是每个个体的能力水平,所谓高的效率,更多来自个人的能力水平。

    但是,所谓个体的比较高的能力水平,恐怕离不开纠错的过程,没有生来就可以写出极少错误代码的人。也就是说,所谓较高的能力水平,大多是通过经验的累积达成的。而这看似虚幻的“经验”,真的愁坏了很多新入门的人。其实,经验完全可以来自测试。或许是自己的测试,或许是别人通过测试,发现的错误,经过整理,从而形成的编码规范。这些规范,有些在人的头脑中,有些固化下来,成为了可供参考的文档。
    而在流水线式的公司,一般来说,这些规范已经形成了文字性的资料,所以新手可以通过阅读这些资料,迅速提高个人的编码质量。
    而在小团体来说,可能更多的是依靠成员之间的默契配合和充分信任。原因不言自明。
    @manuscola

  25. @laoyin
    其实没必要太极端,我们需要测试,尤其是RD,不应该把测试工作留给专职测试人员,我们也应该进行单元测试,这都是对的。但是没必要搞到偏执的程度,非要分支覆盖率达到100%。功能代码50行,测试代码1000行,写代码花了1个小时,测试代码花了2天。

    每个程序员都应该对自己的每行代码负责任,所谓的负责任,不一定就是说通过UT,通过无数测试。我清楚的知道我写的每行代码的作用,我清楚的知道每一个流程分支,我自己测试过自己的代码,OK,没必要必要理什么覆盖率。

  26. 覆盖率仅仅是测试的下限,即使覆盖了100%并不能保证不出BUG。
    至于做不做覆盖率测试文档,是公司文化决定的。
    但是作为一个写程序的人,编码的时候,每当写到 if … else… 的时候,我想TA一定会默默地思考一下,写下的条件是否符合设计要求,达成设计的目的,
    写到循环的时候,一定会仔细斟酌一下跳出循环的条件和时机等等。
    而这一下下的思考,恐怕就是对覆盖率的初步测试了。

    我想,作为一名高水平的编码者,你也是这样做的,不管公司文化要不要求作测试文档。

    @manuscola

  27. When coding on a team, I modify my strategy to carefully test code that we, collectively, tend to get wrong.
    在团队合作编码时,我会稍微改变一下之前的观点,那就是测试多数人都觉得可能发生错误的代码。

    个人理解 -> Kent之前是对个体行为的描述,当团队合作时,判断者是团队,不是某个个体角色。

  28. @manuscola
    很多日企不就这种管理思路吗?我不需要天才的程序员,我只需要一堆能够踏踏实实把我的设计实现成代码的码字工。测试是为了减少码字工偷奸耍滑的机会。
    但是日企至少把握住了最关键的一个地方,设计。相信看过日本人写得详细设计都会很惊讶,这么厚厚一堆纸都写出来了,写详细设计的人干吗不把代码一起写出来?
    其实我也很赞同你的观点,因为我很有幸在程序员生涯之初在一家日企工作,这家企业的管理方式和工作态度让我以后受益匪浅。但是这家企业内部员工的想法都是“有能力的都不会在这家公司待多久”。很可惜,这家公司并不在乎人员流失。这也可以从另一方面来显示,这种管理方面也是有优越性的。

  29. 我还是选择覆盖每个有逻辑的方法,至少保证它是运转正常的。但不会去覆盖每个用例和错误

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注