谢谢大家给了我很多值得思考的问题,下面是我对这些问题的一个回答,希望能有所帮助。最关键的,还是亲手去试一次。
1、如何保证测试代码的正确性?
这是一个很实际,也是很常见的问题。我想这个问题并没有一个非常完美的答案,其实从理论上探讨倒是更像“鸡生蛋,蛋生鸡”的问题。首先要确定的是我们不可能为测试代码再写一个测试代码。其次这个问题的解决可以通过缩小TDD的粒度来解决这个问题。在Kent Beck的书上有专门的讨论粒度的地方,基本上应该先写最简单的代码,保证测试的正确性;然后再通过重构和测试保证代码。
2、测试的粒度应该多大?
没有固定的答案,如果对自己很自信,粒度就可以粗一些;如果遇到错误或者问题比较棘手,粒度就可以小一些。
3、对GUI层开发应用TDD是否利大于弊?
就现在的GUI层测试的工作量来看,如果GUI超过10个(我个人的经验和估算),对GUI的TDD就非常有价值。否则,从成本上考虑就不太合适。
但是随着TDD工具的改进,GUI TDD的成本开始下降,我想也许不久以后,对少量的GUI进行TDD也不会太复杂了。
4、如何在没有TDD习惯和思想的团队推行TDD?
With most men,unbelief in one thing springs from blind belief in another.—Georg Christoph Lichtenberg
所以,在有一两个有TDD经验的人的辅助下,在实际项目中使用TDD进行开发,让每个人都亲身感受一下。我个人的感觉是接受起来并没有当然,team lead必须根据具体情况,根据团队的特点,成员的性格,在项目开始的磨合阶段对TDD的形式作一些修正。当然,最关键的还是两点:每个人有一个开放的心态;以及团队要足够的灵活和健康的讨论氛围。
这个问题就不仅是TDD的问题了,各种软件开发理论都需要回答这个问题。 所以这个问题的答案取决于不同的理论,XP的方式是通过一个User Story驱动多个Test Case。
我昨天晚上回去的时候想了想,下面是我的一些想法,供参考:
1、我感觉这个讲座重点,应该是分析(包括使用代码)我们常用的“先设计,再开发测试的模式”与“测试先行,设计和重构断后的模式”的优缺点,以及他们可能应用的范围,在哪些方面这些模式又有局限,我想不可能有一个万能的模式去解决所有的问题,不论是第一种模式还是第二种模式都会有适用范围的。一种模式完全取代另一种模式的可能性比较小。
1、我感觉这个讲座重点,应该是分析(包括使用代码)我们常用的“先设计,再开发测试的模式”与“测试先行,设计和重构断后的模式”的优缺点,以及他们可能应用的范围,在哪些方面这些模式又有局限,我想不可能有一个万能的模式去解决所有的问题,不论是第一种模式还是第二种模式都会有适用范围的。一种模式完全取代另一种模式的可能性比较小。
2、就我的水平和眼界而言,或者说我的习惯和思维方式而言,我喜欢“先设计,再开发测试的模式”,直到把问题想清楚了再去写代码。对于我这种人,如果要求使用“测试先行,设计和重构断后的模式”,至少应该说服我,这种新的模式比我现在使用的模式好处在什么地方,而且是比较大的优点,否则我不会去改我的习惯,或者没有动力去改变现状。尤其是演示代码的时候,不断的重构,推上,拉下,我回去的时候能够回忆起来的就是一个“乱”字,那我就会本能的不去使用这种新的模式。
3、NUnit测试很重要,这一点估计没有人会反对(Gary,shut up!:-) )。但是到底有多重要,是一个手段,还是到了整个项目驱动器的地位,应该视情况而言吧。
4、可以谈谈NUnit的不足的地方。
5、昨天我和Gary在讲座上说了一些跑题的话,撇开礼貌问题不谈,至少说明讲座没有能够说服我们这种还在使用第一种模式的人;或者讲座不太紧凑,没有抓住听众,吸引听众,引起听众的思考。当然要引起思考很难,尤其是我这种从不思考的听众。又跑题了。
4、可以谈谈NUnit的不足的地方。
5、昨天我和Gary在讲座上说了一些跑题的话,撇开礼貌问题不谈,至少说明讲座没有能够说服我们这种还在使用第一种模式的人;或者讲座不太紧凑,没有抓住听众,吸引听众,引起听众的思考。当然要引起思考很难,尤其是我这种从不思考的听众。又跑题了。
我这个周末稍微想了一下,觉得也许有可能把Design-first的“制度性和确定性”与TDD的“安全性”作一个结合,这个周二我想试验一下这个想法,希望到时候能听到你们的反馈。另外,我还把PPT重新写了一编,加入了更多思考和反馈的内容,但是时间不知道是否足够。这也是为什么每次正式之前有一个预演的原因。
就使用范围来说,我有一个比较极端的观点就是超过1个人2个月的项目,都应该使用TDD。当然,正如我在一开始的时候提到的,TDD更应该成为一种思维方式,而不仅仅限于一种技能。
我觉得Test Driving是一种很好的设计与开发方式,至少是在一些场合来使用是非常适合的。伟大的软件都不设计出来的,而是一点儿一点儿做出来,一次一次的代替,一次一次的反复,是理想与现实的不断冲突和妥协的结果,这是软件开发的现实。Test Driving正是用这样一种面对现实的态度来设计与开发的,是现实主义的;而严密的设计之后,再编程的方式,我认为是理想主义,但理想在现实面前往往都被击得粉碎,我想大家也有同感吧。现实主义和理想主义都有它们各自的用武之地,但现实主义适用于更多的场合。
我觉得Test Driving适用地方:
1 抽象的底层代码,基本的需求是稳定,可以反复的refactor;
2 业务逻辑层的代码,可以在变更后,知道所有业务功能还能不能工作,一方面能refactor,另一方面能在一定程度抵抗需求的变化;
Test Driving不适用的地方:
1 已经非常熟悉的领域,先设计再开发就是很好的方法,直接做好就成了
2 需求很不稳定的业务逻辑层的代码,写了的测试,会因变更的需求,变得无用,成为浪费。
3 界面程序,但这是因为现在的Nunit还不够强大
我觉得Test Driving适用地方:
1 抽象的底层代码,基本的需求是稳定,可以反复的refactor;
2 业务逻辑层的代码,可以在变更后,知道所有业务功能还能不能工作,一方面能refactor,另一方面能在一定程度抵抗需求的变化;
Test Driving不适用的地方:
1 已经非常熟悉的领域,先设计再开发就是很好的方法,直接做好就成了
2 需求很不稳定的业务逻辑层的代码,写了的测试,会因变更的需求,变得无用,成为浪费。
3 界面程序,但这是因为现在的Nunit还不够强大
我想牛刚上面的论述我基本上同意,但是也许是演示的问题,我的总体感觉是大家觉得开发过程非常混乱。其实就我当时的思路是非常清楚的。对于不适用的2,我首先同意有一些测试代码会因为需求变化而没有用了,但是从另外一个角度看,如果需求变动频繁到feature被cut掉的话,说明需求、结构变化也很频繁,这种情况倒是TDD能真正显示作用的。
我个人觉得TDD中间最应该注意的是测试代码本身的正确性和整洁的问题,Test code能否做到Clean和Work非常重要。TDD使用不好的情况下,测试代码很brittle,结果需求一改变,很多测试代码跟着需要改变,给维护测试代码本身也带来了不小的工作量;这就要求测试代码也要不断的refactoring,保证测试的唯一性和有效性。那么,对测试代码的refactoring,由什么机制来保证呢? 有已经有的功能性代码保护,首先确定测试代码是正确的,然后进行重构,如果重构后测试不通过,就说明测试代码出了问题,这里有一个根本原则是绝对不能同时修改功能行代码和测试代码,否则将失去一个正确的参照物。
http://dongxun.mblogger.cn/posts/4848.aspx
http://dongxun.mblogger.cn/posts/4848.aspx