东看西看,加上工作中的一些感触,产生了一些零碎的想法,记在这里。
1. 最糟糕的一些软件工程观念
Steve McConnell前两天到Redmond为他的新书Code Complete 2做宣传,有人在MS internal blog上记载了Steve McConnell的一些谈话内容。
He presented a list of "worst code construction ideas of the '90s and '00s".? He contrasted the '90s "spec everything up front" or "design for unknown requirements" with the '00s "spec nothing up front"? ideas, the '90s "uninformed use of the waterfall model" with the '00s "uninformed use of extreme programming", and the '90s "components will solve everything" with the '00s "outsourcing will solve everything".? This section of the talk wasn't spectacularly informative or maybe it didn't resonate since I haven't been subject to any obnoxious application of extreme programming and the outsourcing I've had knowledge of has been handled well.
我来翻译一下:Steve McConnell列举了'90年代以来最糟糕的一些软件工程观点。九十年代的时候,人们相信,之所以项目进行过程中出现需求变更是因为需求分析没有做好;九十年代,人们很重视在设计中的可扩充性,人们坚持认为项目就是严格应该按照“设计,开发,测试”的阶段进行,人们相信模块化开发,认为将来软件开发一定会变成搭积木。到了二十一世纪,人们走向另一个极端,认为应该抛弃一切软件过程模型改走极限编程,软件外包盛行,有人开始鼓吹设计无用论,提倡边开发、边设计、边修改,还有人迷信重构,反对做任何可扩充性设计。
就我来看,无论是"spec EVERYTHING up front"还是"spec NOTHING up front",都是走极端。软件工程里面还有很多走极端的说法,比如有些人坚持认为,绝对不可以不做unit test。正确的认识世界、改造世界的方法是辩证唯物主义。对待软件工程也是如此。我们要辩证,没有绝对正确和绝对错误的;我们要唯物,要结合具体项目情况来操作,不能空谈一套方法论。
2. Continuously Integration?
前两天听到一个人讲Agile Development。他说到要做continuously integration。他举例说,在他们公司里有一个持续集成的环境。code base里面每一次check-in,这个集成环境都会做一个新build,一旦发现build break,就会报错,停止开发,直到找到build break的原因。
听到这里,我的Bullshit探测器响了:
- 一次Build耗时几十分钟是很正常的事情,每一个check-in都做一次build,来得及么?
- 在某些“复杂”项目里,比如开发的是一个3-tier的系统,需要在一个环境中才能运行起来。对这样的项目进行如此频繁的集成、部署、测试,都要做成自动化,成本上划得来么?
- 一遇到build break就停止开发?难道十几个人几十个人都停下来等上一两个小时?
- 对大的项目,怎么能等到做build的时候再来发现build break呢?应该先local build,然后再check-in,这样能确保正式的build不会break。
3. Test Driven?
Test Driven说,我们在开发一段新的code之前,就要把这段code的unit test写好;我们在开发一个新功能之前,就要把这个新功能的test case和test automation建好。我不同意。我同意在测试前必须根据Functional Spec写好Test Case,但我_不_认为我们_必须_写好test automation和unit test再开发。
假设PM的func spec说,我们要做一根圆的棍子。根据Test Driven,test开发了一个圆的套子作为测试。等到dev把圆的棍子开发出来以后,只要放到圆的套子里面套一套,就知道棍子是不是圆的了。但问题是,谁能保证套子本身是圆的呢?
Ok, 我承认上面这个例子有点抬杠。事实上,从鲁班开始,我们国家的木工就一直用类似的工具。比如,有直角尺。桌子腿和桌子面是不是垂直,用直角尺量一下就知道了。所以,在15%的时间里,我也觉得Test Driven有些道理。据说微软的产品组也在推广Test Driven。我不太清楚,至少还没推广到我这里。
4. Functional Spec不是法律条文
Dev和Test都要做spec review,确保spec中没有歧义、没有矛盾、说得充分清楚。Review的时候,dev要确保Functional Specification是“可开发”的,test要确保spec是“可测试”的。例如,spec说:“季度的客户满意率是每个月客户满意率的平均值”。对于这样的spec,
- dev和test不应该问:“每个月”指的是该季度的每个月还是从年初到当前所有月份。
- dev和test应该问:这是什么平均?是每个月满意率的算术平均,还是根据每月被调查人数进行加权。
Spec不是法律条文,不是数学证明。有很多东西要凭sense去review的。否则就会变成抬杠、挑刺。
5. 关于self-motivate
我现在处于一个很有趣的状态:我的people manager是A,在北京;但在Outlook里面,我的上级给B,也在北京;我所作的项目,在北京这边由C负责;我在项目里做测试,这个项目的测试主管是D,在美国;D太忙,把我的绩效考评交给E来负责,伊也在美国。可以说,我有五个上级,但这五个上级谁都只会关心我一点点,初工作的人遇到这种状况大多会士气很低落,会不适应被放羊。
正确的做法是自己对自己负责,自己管好自己,工作按期交付,遇到问题自己去找各种资源想办法解决,解决不了就寻求上级支持。不要去操心谁到底是自己真正的老板这种问题。操了也白操。
打印 | 张贴于 2004-10-28 17:59:00 | Tag:Software Engineering

留言反馈
1. 错与对的界定是建立在一个范围内的,物理学历史就是一个很好的说明--经典力学、相对论、量子力学。我并不反对争论,但是希望大家在讨论一个问题的时候要讲清楚你所讲论点的适用范围--千万不要说你讲的就是一个能够适用任何范围的论点,不然就贻笑大方了!:)
2. 建立在第一步的基础上,我们在根据各自的实际使用范围使用能够解决问题的方法,每个方法都有自己的优点,实用就好!
3. 讨论的意义在于,我们在同样的适用范围,各自出提出自己的论点将这个东西作更加完美--不可能没有错。
对于tdd到底怎么用!我就不凑热闹了!说的不对的地方还请指教。
ps:海纳百川,有容则大。
oh, I saw a good practice in Agile Development here.
1. developers check in code
2. when code ready or stable, developers tag the code
3. build system starts building for every tagging action. if build error, a serious bug should be discovered and need resolve right away.
4. if build pass, an automatic system start to work to test the build for some very simple functionanlities, just check the build quality.
5. QA selects a best candidate according to the auto-basic test result for a formal testing. report bugs.
One issue in this loop: when the milestone comes, developers check code and tag code very often, and have to wait for long time for a good build.
Solution: restricted check in before big milestone.
-FeiFei
软件工程发展到现在,又发展出许多新观念、新方法。
这些新方法基于原有方法的某些不足而提出的。
人们总试图找出银弹。
在我看来,没有什么方法是放之四海皆准的。
适合自己的才是最好的。
这段话是前两天国际奥委会主席罗格来中国做演讲的时候饮用的。
--
btw,我对这个mozilla的例子所引述的内容保留意见。
至少,我看到tinderbox上是这么说的:Please run the pre-checkin tests, and if you have time the smoketests. Do not checkin until you have appropriate reviews and super-reviews. 所以,这里的check-in已经不能等同于我们平时对code base所作的check-in了。在一般的项目开发过程中段,每人每天都可能发生好几次check-in,这种check-in是不需要经过严格的review和test的。只有到了项目后期,或者已经进入in production阶段了,check-in才会被严格控制,或者必须先进入bug fix的测试分支,然后才merge到main branch。我觉得tinderbox的check-in的严格程度相当于后者。对于这种check-in,当然是可以每个check-in都做一个build的。到了项目后期稳定阶段,为几个bug fix就特地做一个build是时常发生的。当然,mozilla属于on-going的项目,所以不存在后期的说法,只是它一直处于对check-in的严格控制中罢了。
简单的说,用了Unit Test不一定会死,不用Unit Test多半会死。
那么你用还是不用呢?
在我目前这个项目中,web层使用到的后台的function几天一变,后台一变前台就要跟着改(没办法啊,他们目前的设计就这样,用一个Strong Type Dataset在各个层之间传递数据。)
因为没有把变化封闭起来,所以我经常会遇到前几天这个页面还一切OK,过几天就自己挂了...
在各个层Debug半天一看(因为没用Unit Test),原来是后台某个地方改了东西....
所以,最大的问题在于,今天我test了这个页码,一切ok,check in进去了,可包不其哪天这个页面会出现bug...
但如果采用的Unit Test,至少我至少可以清楚的知道,整个project里面,那些东西现在是可以工作的,那些是不行的。
所以,为了能让我安安心心的往前走,而不是时时担心以前的model出问题,就必须使用Unit Test。
这也是对于目前这个项目,没有办法的办法了
我所说的传统软件工程是指相对敏捷方法而言的重型方法,或者经典软件工程。:)
如果一个人坚持世界的本源是精神,别一方面又坚持他说的是马克思主义。那么我向别人证明他说的不是马克思主义,并不是无意义的事。因为马克思主义立论的根本就是坚持世界的本源是物质。如果否定这个,显然他讲的不是马克思主义。 :p
你把TDD架接在与XP的本质相矛盾的开发过程之中,当然会哪里都不对。比如XP里没有spec,只有user story和test case。
间接经验的正确性往往还是要经过实践的检验,尤其是当你对间接经验产生疑问的时候。
好了,不争论下去了,再讲下去就是信仰之争了,那很无聊的,因为没有正确不正确的分别。有空在这里争论,不如亲身去感受一下XP,会比较有益处。
“你们站在传统的软件工程上立场来看待TDD”——何为传统的软件工程?瀑布模型么?这两年我一直在我的项目里运用daily build、test case first、scm、change management、base line等等等等各种方法。这些都仅仅是“传统软件工程”么?
关于你所说的,“从你们的发言可以看出你们没并没有真正实践过XP中的TDD,你们评论的TDD只是你们臆想中的TDD,而不是XP中的TDD”——人认识世界并不一定要事必躬亲。
你一直想证明别人对TDD的理解是错的,你自己的理解是正确的。但是这有意义么?这让我想起很早以前也有人试图证明他对马列主义的理解才是正确的,别人对马列主义的理解是错误的。
我当然也相信Test可以指导Design,看从哪方面来说了。Tester在创建test case的过程对spec的review,检查spec是不是明确、无矛盾、“可测试”,也是很重要的一种帮助design的手段。
ok,求同存异吧
依稀记得是不是Martin Fowler说过,
Teams should grow their own processes, using published processes as advice rather than as standards to follow.
共勉 :)
偶承认,偶素保守派,偶素保皇党...
偶是比较习惯先想好设计(如果有的话),再写测试用例(如果我以后会养成写测试用例的习惯的话)。从你的描述来看,tdd里面是边想设计边写test case的。 不知道是不是酱紫呢?
回家就去看看书架上面有没有agile sotfware development,没有的话马上去订 ;)
我觉得已经没有必要再讨论下去了,因为我们对TDD的理解在本质上是不同的。就像一个唯心主义者看待唯物主义的论点,当然怎么看都是谬论。然而信仰何种思想本没有所谓对错,所以讨论下去是无意义。
你们站在传统的软件工程上立场来看待TDD。从你们的发言可以看出你们没并没有真正实践过XP中的TDD,你们评论的TDD只是你们臆想中的TDD,而不是XP中的TDD。而我觉得对于没有亲身使用过的东西,你可以选择不相信它是有效,但是妄加评论说它是无效的,则是一种很不负责任的行为。
站在相信XP是有效的这一立场上,我认为“TDD的重点在于把测试当作一种设计的手段”并不是表象,写测试确实对设计具有指导作用。具体的例子看过《Agile Software Development》中的第六章,然后再“亲手”实践一下,就会有所感触。
你们愿意相信“Test无论如何提前,也得落在真正的的design后面”,我愿意相信“Test可以指导Design”,这无所谓对错,但是我衷心希望你们能够不要再指责XP中的TDD了,因为你们所指责的根本就不是TDD。
@肖波
“只是我们在实践的过程中一早就发现单独使用TDD根本就没意义,开发人员自己做测试用例不自觉的就会不测试那些自己认为很难实现的东西”
请你不要再把自己发明的东西套上TDD的名称了。这样除了混淆视听或者表明你很赶时髦地知道TDD这个名词之外,根本就没有意义!
问题是持续集成并非仅仅是代码级集成.
看来还是我看走眼的情况下有所先入为主:D, 现在看来其实你是批判那种持续集成的做法. 这点我自然是同意的.
我所举例子也仅仅是为了说明持续集成其实是发生在任何时候的.
又及,“个人化的check in 和 持续集成”和在整个团队环境中那种频繁的集成是完全两样的,ok?
我自己写程序也有一个习惯,写几段就会习惯性的ctrl+shift+b一下。我也在做“人肉”个人化持续继承呢
:D:D:D, 这个blog标题叫做《最糟糕的一些软件工程观念》。这个标题取自blog中第一个小标题。
该不是你认为我把tdd和持续继承都纳入“最糟糕的一些软件工程观念 ”范围里面了吧?
如果是这样,
:D:D
——是读的人看走了眼,还是写的人没写好?
——如此说来,“5. 关于self-motivate”也算是“最糟糕的一些软件工程观念 ”?风马牛不相及啊
正是因为个人认为你的这个Post会产生一些误导. 所以才想说你所处的环境造成的你的这种想法.即所有结论都是在一定环境下得出的结论.
正式因为个人认为你的这个Post会产生一些误导. 所以才向说你所处的环境造成的你的这种想法.即所有结论都是在一定环境下得出的结论.
你的标题实在太武断. 持续集成和TDD都被你说成最糟糕的. 就像买了100个苹果, 拿出的第一个苹果是坏的,然后就说所有的苹果都是坏的一样.
其实就像Eclipse,有个功能,在每次save的时候自动编译. 可以把它看成个人化的check in 和 持续集成.
即使在极端的情况下使用某种方法论的时候, 亦会在理解上或者在某些情况下产生让你的bullshit探测器反应.
“既不左倾又不右倾,难道不也是倾么?”——没错。中间派也是一个派系。我就是走中间派的,反对走极端,反对绝对的spec nothing,也反对绝对的spec everything。
“你每次Build的时候都是Build所有东西么?什么年代?”——我说过每次build都要build所有东西么?你从我的哪句话里面推断出来的?我稀里糊涂就被你challenge了。
关于check-in,那个人嘴里的check-in就是对code base的check-in,他自己这么说的,他自己说就是source control里面的check-in。我听到他说的话了,你没没有听到,你却来challenge我听错了。
况且,我不记得在软件工程领域还有其他什么地方用到check-in这个术语了。就算有,也是鲜见。
关于tdd和套子的例子,请不要再追着这个例子说事,ok? 举例子是为了更形象的表达某一个意思。没有必要对着例子本身横挑鼻子竖挑眼的。
spec最主要的目的就是两个:沟通、契约。我不觉得你说得和我说的有什么大的出入的地方。
:), 太正常了。说心里话,我也认为,NUnit对于作Solution来说没什么用出。项目和项目之间也是有很大区别的。我想,做一个办公自动化项目和做一个Codec肯定不一样。Codec的项目比OA更需要unit test.
2. 你每次Build的时候都是Build所有东西么?什么年代?即使是使用.Net,良好的模块划分,需要你Build所有东西么? 另外你是如何你所听说的那个故事中的check in?理解为类似ssf还是cvs中的check in动作?难道不可以使理解为其他的check in动作?
3. TDD. Development这个事情包含的东西太多了, 可不仅仅是做个套子让东西套进去. 比如现在要你做个成为XXX形状的物体,然后你不知道XXX形状到底要怎么做。OK,我先思考XXX形状物体的应用环境,从应用环境来思考XXX的实现。
4. 诚然spec不是法律,但如果沟通不好,就要扣你薪水。虽然我只需要user story。
偶以为Test无论如何提前,也得落在真正的的design后面。不知道我们说的是不是一回事?
关于“TDD的重点在于把测试当作一种设计的手段”,那也只是表象而已,test case不是真正的设计,设计就是设计,也许通过test case可以体现出设计,但是test case不是设计。
------
我觉得这段话我同意。无论如何,一定先有一份功能设计文档(在我们公司叫做Functional Spec),然后才有Test case,然后才有test automation(包括unit test & UI automation、Performance prototype/script等)。
--
有一点我想是肯定的:不能没有一份自然语言写成的Functional Spec(或者叫做概要设计什么的)。
谁说单独使用TDD没有意义的?你去Sourceforge上看看,很多成功项目都是一个人做的,里面的TestCase可都是一个人写的,能说没有意义吗?写TestCase很重要的意义是可以用于回归测试,这样可以一定程度上保证重构的正确性。而且也并不是说一定要编码前写,很多时候TestCase是Tester发现了bug后,开发人员修正后写的。至于你说的分开写代码和TestCase,我认为好像应该划到Pair编程的方面来看,其实结对编程的好处和给机器做冗余提高系统可用度的原理是一样的,无非就是100%-(1-90%)*(1-90%)=99%=更高的可用度和质量而已。我个人觉得,Pair编程比较适合于代码质量要求比较高的核心框架的编写,对于业务系统来说性价比不高。
微软其实的确应该推推TDD了,我去年工作时接触过几个的微软的顾问,连NUnit都不清楚,做项目测试时,还是主要用Mercury的自动化工具结合手工测试来做。
但我在实际应用中没有过于强调TDD的观念
对于每个程序员来说,他可以用TDD开始他的代码和实现,也可以不用,TDD更多的时候能消除他的恐惧感和提高他的设计和实现能力。
但如果他CC结束,我们一般会要求他完成自己的代码自测,这个会在Code Review中被大家检查,NUnit的测试类表面他做了多少的自测。并且我们有一个工具可以让程序员在修改代码后,自动的测试他所有的测试类(代码测试覆盖率)
TDD 在这里并没有体现,也很难体现,但如果细心,所有的Dev Leader都可以从测试案例里看出来,他的代码是TDD出来还是后补的。我认为,这个分别不大,随个人。
最后测试或SCM的Team会在每日的时候,再做他们自己的测试案例,主要是根据功能和后台无界面的,其中有一些可能从程序员的测试案例中拿出来,也可能使用自己的。但编译完成后,BVT的过程会自动再把这些测试案例自动做一遍,最后再决定是否将这个版本提交给测试Team去测试
我想这需要在一个整个的开发流程中来看,现在虽然都是自动的,但是在数量上我们还要做许多手工的工作,才可能获得测试代码的覆盖率和功能通过的覆盖率这两个值,我想下一个版本的VS在代码覆盖率方面可能会更自动和有帮助,如果功能覆盖率也能很好的得到帮助,那么就更好了
至于上述的MSF/MOF的论证,我想如果都是微软的,大家应该回去看看MSF4.0再说:)
而是两个人一起写代码,一起写单元测试
没有什么太多的理论,只是经验,XP本身也正是实际中总结出来的
开发人员自己做测试用例不自觉的就会不测试那些自己认为很难实现的东西
这正是因为没有进行结对编程所造成的后果
赞同你的"结合论",TDD脱离了XP确实没有意义,比如你做了TDD,但是却不进行结对编程的话,没有人会提醒你忽略了的东西,于是就出现了上面那种结果
tdd里并没有从物理强行要求用两个不同的人来做,你读书读得非常仔细,只是我们在实践的过程中一早就发现单独使用TDD根本就没意义,开发人员自己做测试用例不自觉的就会不测试那些自己认为很难实现的东西,所以TDD只能是XP里的一部分,不能单独存在。其实偶也想问你为什么你认为TDD里测试和代码是一个人做的?如果有,请赐教实践经验。成对编程你以为就是两个人在一台机器上写同一段同一段测试用例?理论上来做是可以。不过在我的团队里,只有在测试失败的时候两个人坐在才是必要的,。
我不知道你的公司是不是按你说的那样做的,我的团队从来就是两的人两台机器,我从来反对两个人使用一台机器做开发和测试。如果你有最佳实践经验,还请赐教
xiaobo:
关于微软 vs TDD,偶听人说:“据说微软的产品组也在推广Test Driven。我不太清楚,至少还没推广到我这里。”,和你说的“我可以明确告诉你,微软从来没有以后也不会推广TDD”好像有点不同奥。
关于偶的语气,介个,说话总是要有点语气di,偶看到银弹总会忍不住上去摸两把;)
Justin:
偶以为Test无论如何提前,也得落在真正的的design后面。不知道我们说的是不是一回事?
关于“TDD的重点在于把测试当作一种设计的手段”,那也只是表象而已,test case不是真正的设计,设计就是设计,也许通过test case可以体现出设计,但是test case不是设计。
大牛往往都酱紫,明明打了腹稿,偏偏告诉你他出口成章:) 也许不是有意为之,但人家十几二十年的开发经验在哪里呢。
“TDD的核心是test-first design”,照偶理解就素牛人,想啊想,大到系统架构怎么搭,小到局部的对象如何交互,想好了后,开始写test case。不知道偶得理解又没有什么偏差。偶总觉得,介个东东不是粉自然的流程么?难道先设计test case,再设计系统架构不成。
偶素小毛头,介个TDD,偶学不来 ;)
TDD并不是一个流程,也就是说仅仅把写测试放到写代码之前,并不能被称为TDD。我在前面的回帖中已经说过,如果仍然使用传统的测试与开发分离的过程,把写test case的时机提到写代码之前,根本没有本质上的差别。TDD的重点在于把测试当作一种设计的手段,这也正是TDD中要把测试提前的关键,因为设计总是先于编码的。
在《Agile Software Development》一书的第四章,Bob大叔一开始就说:The act of writing a unit test is more an act of design than of verification.在这一点上TDD是具有革新性的。
所以TDD的核心是test-first design。这点没有真正实践过的话,是很难理解的。你可以看一下《Agile Software Development》的第六章,那里有一个完整的开发过程的例子。
如果仍然用传统的眼光来看待TDD,只把它看成开发流程上的更改,就会使得对TDD的理解产生偏差。
我实在忍不住了:你是从哪里看来TDD的测试和代码是由两个不同的人写的?请一定不吝赐教。
在XP的团队中,实践TDD时,Unit Test和代码的编写是由同“一个”pair来完成的。如果测试和代码是由两个不同的人分开写的话,TDD的意义就完全被抹杀了。
如果你说的TDD不是XP中的TDD,那我没话讲。
我可以明确告诉你,微软从来没有以后也不会推广TDD,微软为软件开发生命周期的管理提供了两个框架,一个是MSF,这主要针对软件开发的生命周期,一个是MOF,MOF针对的更大一点,是有关IT服务的,MSF的一部分工作必须采用MOF的思想来做。
你的这种语气其实我以前也有过,大意如下:SAP有什么了不起,不就用SQL把数据从这里导到那里
如果光从这个函数本身来说,Assert.IsTrue(add(1,1)==2)这个测试用例确实如你说是正确的,但不是完备的
一个完备的测试用例应该怎么定义应该看测试人员怎么的功力,这得看在设计之处的规范是怎么定义的,有没有边界条件,测试是否覆盖了所有分支,同我上面所述的add函数,在头脑风暴中就应该确定两个输入参数的范围,如果在设计规范中已经确定了a,b两个数只可能出现1,那么这个测试用例就是正确的,如果a,b两个数可以出现1,2,那么虽然这个测试用例不是完备的的,但是通过测试了,那么这段代码就是通过双方都认可的,从某种程度上就应该是被release的代码,即使它有bug,我为什么一再强调开发和测试必须是两个人,就是因为同一段代码需要两个人从不同的角度去验证它,这样才能保证代码的质量。至于你认为的测试用例不完备造成的bug,我上面已经说过了,测试用例并不要求是完美的,只是保证代码通过单元测试能将bug降低到最低,经过tdd产生的bug率=开发人员代码的bug率*测试人员的bug率,你一直不看这个公式,我只能给你举个例子,如果开发人员每100行代码平均会带来1个bug,那么他的bug率就为0.01/loc,那么一个5000行代码的程序理论上应该有5000*0.01=50,TDD中的测试人员的bug率假定也是0.01/loc,那么经过tdd后的上述的程序的bug数就5000*0.01*0.01=0.5,也就是说,通过增加一个人员成本,将50个bug降低到了0.5个bug
to 4drush
tdd确实是一种比较极端的方法,但我个人从实践中认为在3-20个人的团队中使用是比较符合当前的软件开发行业的,但是tdd在实践中你必须放在xp的大环境中来考虑,单独的tdd并不能保证你的软件能够正常发布,他只保证了你的程序是符合你的设计规范的
我觉得这应该是Justin Shen想要表达的意思
在MVM的这个例子中,直角尺在做木工的整个过程中一般是不会被改变的,虽然有点类似,但是直角尺不能完全等同于TDD的测试用例
在XP中,TDD确实是作为设计存在的,还是举套子的例子
在软件设计初期,我们可以先认为棍子应该是圆的,我们写了一个测试棍子是否为圆的套子,过了一段时间,我们发现棍子不能仅仅是圆的,还应该有一条凹槽,我们又修改了套子,加上了一个测试凹槽,又过了一段时间....
在RUP这种以文档驱动的设计方法中,这种变化可以用文档沉淀下来,但是XP中文档的地位并不高,设计过程是通过测试用例体现出来的,既然程序代码就是程序员最好的语言,为什么要写文档?
TDD是一种比较极端的方法论,至于是否完全正确,我不知道,也没有资格去评论(总结出XP的绝对是大师),但是无论如何,XP中就是这么说的.
偶怎么觉得TDD只是一种开发的方法,或者说流程而已。这玩意有这么神么?连微软都跟风咯,看来,市场确实缺少好的概念来刺激了,连oo、uml、rup甚至xp都提不起大家的精神啦。tdd这么一个小小的topic上都出这么多书,mvm顺便提到一个tdd,也引来不少回复;)
嗯 我得承认你说的是对的。另外,我的一些批评确实比较草率。
在我的观点里,利用在TDD一开始写的Unit Test,来保证程序的正确性,根本是做不到的。程序的正确性即使加上后期完备的测试都不可能完全保证。尤其TDD中所写的test case通常都是最一般情况下的代码使用方法。所以以为先写了test case,自己的代码就在一个防护网中不会出错的想法,在我看来有点天真。
所以在我个人的观点里,TDD是意义在于提供了一个新的审视你的设计的角度。TDD中写的Unit Test的作用并不是为了保证程序的正确性(它也无能为力),而在于让你在还没有开始编码之前,就站在用户的立场来看待你将要编写的模块。
因此,我会比较强调TDD中Development的一面,而尽量回避Test的一面。诚然使用TDD时,比较完备的Unit Test体系会有效的防止在重构时犯的低级错误,但是既然这些Unit Test不能保证原来的代码是真确的,当然更不能保证重构后的代码是正确的。“因为我使用了TDD,所以我开发出来的代码就是正确的”--我觉得这是一种非常危险的想法。既然TDD作为测试的功效很有限,那不如撇开它的测试的功能而专注于它对设计的提示作用?
所以我原本要表达的可能只是:检查那个棍子是不是圆的,本不是TDD应该去做的事,也不是TDD能做得到的事。就像你说的,怎么能保证套子是圆的呢?但是这并不意味着TDD就是没有意义的。在我的观点里TDD的意义在于另一面,在于它对设计的提示作用,在于它所提供的审视你的设计的另一种角度。
所以说,或许我们的观点大部分是一致的,只是我觉得TDD有其它的意义在。
最后,关于你引的那个帖子,我觉得 龙惊蛰 的观点可能确实和我比较接近。另外在那个帖子里龙惊蛰的批评似乎并不应该是针对孙展波的?而应该是和展波一样针对帖主的“用上面的方法进行开发,确实是确保正确的最快的做法。”这句话而说的吧?
即便Assert.IsTrue(add(1,1)==2) 是一个正确的测试用例,但不是完备的。
assert(1+1=2)通过,
并不等于assert(2+2)=4就一定能通过。
assert(2+2)=4通过了,
并不等于assert(3+3)=6就一定能通过。
如此往复,无穷尽也。
问题就出在f=x+y是一根曲线。对于f'(x,y),仅仅验证f'有某几个点与f重合,是不足以证明整个f'和f完全重合的。
换句话来说,对于f(x,y)=x+y这样一个design来说,test case永远是不够的。
这时候就只能做code review,或者索性就让他不完备。时间和金钱不允许我们做到完备。
:(胡言乱语了一些,希望能表达出我的意思。
通过增加测试程序使程序更正确,其投入和见效,并不是线性增长的。
确实不是线形增长的,其投入和见效是一条抛物线,也就是说使用TDD的Bug率=开发人员的Bug率*测试人员的Bug率
相信对于TDD有如此多疑问的人看不懂此公式吧
int add(int a,int)
开发人员的写是
return a+b;
测试人员是
Assert.IsTrue(add(1,1)==3)
这样的程序编译通过了,测试能通过吗?显然不行。问题在哪里?我们大家都能很明白是测试人员的测试用例错了,所以,对于每一个fail test,对开发人员和测试人员都有责任,并不是单边的开发人员的错误,必须两方坐在一起讨论错误的原因,只有两方坐在一起的时候才能发现问题所在
1、TDD必须要保证你自己做的程序必须是可测试的,如果你程序做得多得话,自然会发现程序做得怎么样是可测试,什么样的程序是不可测试的
2、写测试用例的人和写代码的人不能是同一人,写测试用例的作用不是帮助开发人员理清开发的头绪,是用来验证开发人员的承诺是否已经达成,至于测试的人和开发的人怎么达成目标一致,可以通过头脑风暴的方式,TDD的这种工作方式的延伸到极限编程里就是成对编程,一个人开发,一个人测试或者一个人编码,一个人review,极限编程通过这种方式来保证设计和编码的质量
套用你的例子,TDD并不是想你想的那样先做好一个模子,然后把铁水浇进去那样,因此你会问为什么模子一定是正确的,我的回答是在TDD里实际上是一个人在做圆的铁棒的模子,一个人在做圆的铁棒,当铁棒能够正好凑到模子里的话则铁棒一定是圆的,一个熟练的人做圆的模子的质量不过关有一定的概率,一个熟练的人做圆的铁棒的质量不过关也有一定的概率,但是两者都做错的概率是两者的概率的乘积,因此质量远远高过传统方法。
我个人可能对词汇比较敏感,因为我认为从一个人所选用的词汇常常能看出他潜意识中对问题的某些看法。就好象虽然你给Test Driven链接的网页上用了“Test Driven Development”,但你却选择忽略最后这个词一样。当然这只是个人看法,你完全可以认为这是无稽之谈。我只是习惯性的把这个作为我回帖的切入点,如果引起你的不快,我表示十分抱歉,我发誓以后不再在这个词上纠缠了。
事实上,是你之后作的那个比喻,才给我强烈的你没有把TDD当作一种开发方法的感觉。
“根据Test Driven,test开发了一个圆的套子作为测试。等到dev把圆的棍子开发出来以后,只要放到圆的套子里面套一套,就知道棍子是不是圆的了。”
在你的这个比喻中,test做好套子之后,就等着dev把棍子开发出来,这个套子唯一的用途只是在最后做检测,却并没有在dev开发的时候起到任何作用。这和传统的测试有什么两样?哪里称得上开发方法?
所以说你所作的比喻说的仍然只是传统的测试过程,只不过把写test case的时机放在dev写代码之前罢了。这当然就显得很可笑,因为对于传统的测试过程来说写test case和写代码完全是不相干的两件事(都依据func spec),谁先谁后有什么关系?任何一个没有写好都不能开始测试。
然而TDD是不同的,TDD中写test case和code的是同一个人,写test case的作用是为了帮开发人员厘清开发的头绪,真正弄清楚将要写的代码是怎样被其它模块使用的。
我打个比方,TDD就好象我们在制作一件铁器,我们首先做好一个模子(编写test case),然后把铁水浇进去(实现test case中用到的函数),冷却之后就做成一个铁器。
为什么做棍子和做铁器的时候用了不同的方法?这就是我说的适用范围的问题。XP有自己的适用范围,并不能适合所有的项目。
多谢指正。
btw,关于你要说的"没有什么Test Driven,只有Test Driven Development",我不知道你是否注意到我给"Test Driven"加的link是指向哪里的。
有时候,讨论到软件工程方法,我也听到有人说话说得快的时候常常会说"extreme如何如何",或者说"Agile如何如何"。我当然能听出来他是用了省略的说法来,我不会把他嘴里的"Extreme"理解成其他东西。
you sayd: "TDD(Test Driven Development)从本质上是一种设计的方法,而不是一种测试的方法"。你认为我有可能把Test Driven理解成一种测试的方法么?Test Driven Testing? Object Oriented Object? Web-based Web? Semantic based semantic? ...
ok, anyway, 多谢指出我表达上的不周。
我个人觉得自己不了解的东西,还是不评论为好.
另外:凡事不能走极端这点我是同意的。“设计已死”这样的话永远也只能当作是一个口号。真正学习一下XP,就知道在XP里,只是换了一个方法来设计罢了。而且XP本身也有适用的范围,并没有什么银弹。