数码控科技猎奇Iphone动漫星座游戏电竞lolcosplay王者荣耀攻略allcnewsBLOGNEWSBLOGASKBLOGBLOGZSK全部技术问答问答技术问答it问答代码软件新闻开发博客电脑/网络手机/数码笔记本电脑互联网操作系统软件硬件编程开发360产品资源分享电脑知识文档中心IT全部全部分类全部分类技术牛文全部分类教程最新网页制作cms教程平面设计媒体动画操作系统网站运营网络安全服务器教程数据库工具网络安全软件教学vbscript正则表达式javascript批处理更多»编程更新教程更新游戏更新allitnewsJava新闻网络医疗信息化安全创业站长电商科技访谈域名会议专栏创业动态融资创投创业学院 / 产品经理创业公司人物访谈营销开发数据库服务器系统虚拟化云计算嵌入式移动开发作业作业1常见软件all电脑网络手机数码生活游戏体育运动明星影音休闲爱好文化艺术社会民生教育科学医疗健康金融管理情感社交地区其他电脑互联网软件硬件编程开发360相关产品手机平板其他电子产品摄影器材360硬件通讯智能设备购物时尚生活常识美容塑身服装服饰出行旅游交通汽车购房置业家居装修美食烹饪单机电脑游戏网页游戏电视游戏桌游棋牌游戏手机游戏小游戏掌机游戏客户端游戏集体游戏其他游戏体育赛事篮球足球其他运动球类运动赛车健身运动运动用品影视娱乐人物音乐动漫摄影摄像收藏宠物幽默搞笑起名花鸟鱼虫茶艺彩票星座占卜书画美术舞蹈小说图书器乐声乐小品相声戏剧戏曲手工艺品历史话题时事政治就业职场军事国防节日风俗法律法规宗教礼仪礼节自然灾害360维权社会人物升学入学人文社科外语资格考试公务员留学出国家庭教育学习方法语文物理生物工程学农业数学化学健康知识心理健康孕育早教内科外科妇产科儿科皮肤科五官科男科整形中医药品传染科其他疾病医院两性肿瘤科创业投资企业管理财务税务银行股票金融理财基金债券保险贸易商务文书国民经济爱情婚姻家庭烦恼北京上海重庆天津黑龙江吉林辽宁河北内蒙古山西陕西宁夏甘肃青海新疆西藏四川贵州云南河南湖北湖南山东江苏浙江安徽江西福建广东广西海南香港澳门台湾海外地区

推行TDD的思考

来源:CNBLOGS  责任编辑:小易  

  目前来看,推行TDD的障碍大约有如下几点:

1. 开发人员的质量意识;
2. 分析需求并进行任务分解的能力;
3. 将测试作为开发起点的开发习惯;
4. 开发人员的重构能力,包括如何识别坏味道和如何运用重构手法;
5. 单元测试的基础设施,尤其是测试数据准备;

  开发人员的质量意识

  开发人员对于软件质量,常常偏重于软件的外部质量,体现在他们的工作效益上,就是被测试人员发现的缺陷数。而惯常的软件开发思想,总是认为开发人员不适合做测试,因为他们总是站在自己的角度去看待问题,从而可能忽略真正需要测试的用例。这种思想给了开发人员一个错误信号,认为自己不应该写测试,即使写了测试,也写不好。殊不知,由开发人员编写测试带来的收益,最重要的一点不在于测试本身,而在于它能促进开发、测试以及需求分析人员的交流与沟通。而测试先行的方式也能让开发者跳出实现的窠臼,而从业务角度去看待问题,从消费者角度去思量接口。此外,由于开发者总是惫懒地将测试职责委派给了专门的测试人员,于是渐渐会产生一种依赖心理。测试人员的精确测试当然可以保障质量,但这种测试通常是黑盒测试,这里保障的质量主要还是外部质量。而且,这种测试带来的反馈总是慢于开发进度,一旦发现缺陷,修复缺陷的成本也会变得更高。

  软件质量除了外部质量之外,内部质量同等重要。软件成本等于开发成本与维护成本之和,而维护成本的增加主要就归咎于内部质量的糟糕。这里讲的内部质量包括:代码的可读性、可重用性、可扩展性等。当我们让开发人员为原有代码编写单元测试时,总是觉得举步维艰。分析原因,主要问题在于代码的可测试性不好。要测试一个类,竟然连简单创建它的对象都变成了不可能完成的任务。我们为这样的代码编写单元测试,就好像在触及蜘蛛网,一旦被这些网丝给牵住,缠住,就可能无法摆脱。除非我们能够快刀斩乱麻,那对于这个系统而言,就不是维护,而是重写了。测试先行的开发至少在一定程度规避了这样的问题。即使代码的内部质量仍有所欠缺,但在足够覆盖率的保护下,我们要进行重构也变得更为简单。

  然而,这些好处都不是短期能够见到成效的,且团队若不能达成共识,只靠一二人坚定地践行TDD,在测试覆盖率不够的情况下,改进仍然有限。多数开发者在维护别人的丑陋代码时,可能会骂声连连,殊不知同时作为骂者自身,其实也在重复被骂者的故事。

  我不是说没有采用TDD,代码质量就一定不高;但我可以说采用了TDD,代码质量至少有了可以改进的基础。

  分析需求并进行任务分解的能力

  需求分析能力常常是开发人员的短板。开发人员养成了一个习惯,看什么事情都会从技术实现的角度去思考。要实现一个网页,就会想到如何编写JavaScript来响应用户的动作,如何编写CSS,却不会去思考用户体验和操作的流程。要完成一个数据分析,总会想到数据的属性,转换和提取数据的算法,却不会想到分析数据的价值以及合理的流程。

  而且对于繁琐的需求描述,我们总没有耐心去深入研读,而是会在掌握了大体意思后,就开始匆匆进行开发与实现。TDD要求我们在编写测试之前要做好合理的任务分解。若没有很好地理解需求,任务分解就无法顺利的进行。

  这就带来了团队协作的问题。若我们能从需求的源头上进行改进,或许TDD会变得更容易。例如,我们对故事的拆分更合理,较好地遵循了User Story的INVEST原则,那么我们所要实现的Story在测试性、独立性方面都会有很好的改观。如果BA能够非常明确地编写出验收条件(Acceptance Cretiria),进行任务分解就变得更加容易了。

  更进一步,若BA能够参考甚至遵循Specification By Example,并采用Given-When-Then的模式来描绘各个用例场景,再要进行任务分解,不就变得轻而易举吗?因此,有时候推行TDD非常艰难,或许最大的原因是我们仅仅将目光放到了开发者身上,而忽略了BA扮演的关键角色。正所谓:问渠那得清如许,为有源头活水来。

  我一直强调任务分解是有层次的。分析需求时,不能一个猛子就扎进繁琐的实现细节。要从用户价值出发,先梳理出最外层的需求任务,然后抽丝剥茧,条分缕析地层层递进,如此方能理清思路,掌控复杂逻辑。基本上,任务分解可以分为三个层次,即业务价值——>业务功能——>业务实现。并且这个层次是一种“递归”的状态,视需求的复杂度而定。

  将测试作为开发起点的开发习惯

  再说说开发习惯的问题。这种改变显然不是一朝一夕可以完成的。以我个人的经验以及我所观察到的情况来看,固然是习惯的力量在作祟,然而主因还是因为对TDD方法的掌握程度以及一些误解导致。

  前面已经述及,任务分解应该是TDD的起点。多数开发者未能形成任务分解的习惯。因此在改变为测试先行的时候,错以为应该一上来就写测试。因为思路没有理清,脑子里是一片乱麻,再加上本身对TDD不够熟悉,于是编写测试就变得举步维艰,总觉得束手束脚,就好像被绑了一只手,又好像是在泥沼中挣扎。许多时候,甚至发挥不出自己的哪怕三分的功力。

  一贯以来,我们都在强调测试先行,测试先行……容易产生一种错觉,就是认为TDD必须一开始就写测试,“简单设计”嘛,于是就没有了设计。这让那些习惯于事先设计的开发者更难以接受。

  以下是我对于“TDD是否需要事先设计”的个人观点:

Martin Fowler的文章Is Design Dead?其实就是对此问题的正本清源。我个人认为,视场景而定,测试驱动开发仍可进行事先设计。设计并不仅包含技术层面的设计如对OO思想乃至设计模式的运用,它本身还包括对需求的分析与建模。若不分析需求就开始编写测试,就好像没有搞清楚要去的地方,就开始快步前行,最后发现南辕北辙。测试驱动开发提倡的任务分解,实际上就是一种需求的分析。而如何寻找职责,以及识别职责的承担者则可以视为建模设计。测试驱动更像是一种培养设计专注力的手段,就像冥想者通过盘腿静坐的手段来体悟天地一样,测试驱动可以强迫你站在测试的角度(就是使用者的角度)去思考接口,如此才能设计出表现意图的接口。但编写测试自身并不能取代设计,正如盘腿静坐并不等于就是冥想。

在开始测试驱动开发之前,做适度的事先设计,还有利于我们仔细思考技术实现的解决方案。它与测试驱动接口的设计并不相悖。解决方案或许属于实现层面,若过早思考实现,会干扰我们对接口的判断;但完全不理会实现,又可能导致设计方向的走偏。举例来说,如果我们要实现XML消息到Java对象的转换。一种解决方案是通过jaxb将消息转换为Java对象,然后再定义转换映射的Transformer,通过硬编码或者反射的方式将其转换为相关的领域对象。然后在执行了业务操作后,再将返回的结果转换为另一个Jaxb对象。而另一种解决方案则是通过引入模板,例如StringTemplate或者Velocity,定义转换的模板,然后进行替换实现。这两种解决方案的区别,直接影响了我们划分任务的方式。所以在运用TDD时,先不要一巴掌拍死,可以先抱着开放的态度尝试尝试。何况,TDD并非一招鲜,吃遍天,总要有适合它的场景。例如UI的开发,交互协作的控制逻辑,数据库开发,并发处理,都不是运用TDD的太好场景。

  开发人员的重构能力

  TDD的核心是红——绿——重构。这意味着重构是TDD非常重要的一环,它直接关系到TDD开发出来的代码质量。没有好的重构能力,TDD就会有缺失。若说代码的内部质量是生命的话,重构就是灵魂,缺少了它,代码就没有灵性了。多数时候实施TDD,都会因为重构能力的缺乏而陷入困境。

  重构的关键首先在于如何识别代码的坏味道。这需要代码阅读的千锤百炼,而非死记硬背老马总结的坏味道。当这些坏味道变成你的一种直觉,甚至就像与生俱来的一种能力时,你就会降低对糟糕代码的容忍度。在你眼中,这些烂代码就是垃圾,必须清扫,否则无法“安居”。

  重构手法与代码坏味道一一对应。若有测试保障,重构就变得安全。但尽可能地,我们还是希望运用工具提供的自动重构功能,这既提高了重构效率,也在一定程度下确保了重构的安全。

  当然,重要的是要找到重构的节奏感,即小步前行,每次重构必运行测试的良好习惯。若能结合分布式版本管理系统,做到原子提交,就会更加方便。即使重构出现问题,我也可以快速地回到前面的版本快照。

  在TDD过程中,若能结对自然是上佳选择。当一个人在掌控键盘时,另一个人就可以重点关注代码的可读性,看看代码是否散发出臭味。两个人的眼睛终归要更锐利一些,至少视野的范围更广泛。

  及时重构是重构诸多实践中最重要的一点。不要让重构成为你在未来偿还债务的杀手锏。越拖到后面,偿还债务的成本就越高。以重构而论,可能需要的重构能力就更强,因为重构变得复杂了。当然,只要你的代码能够保证足够的覆盖率,以及较好的松散耦合,重构依旧可行。采用TDD,基本能满足这两条要求。但以成本而论,小步前行才是重构之道。

  单元测试的基础设施

  最后说说单元测试的基本设施。很多时候,这可能不是问题;但很多时候,这可能会成为大问题。面对诸如测试数据准备等问题,需要认真分析,找到应对方案。原则上最好能找到一些开源的测试框架,包括生成测试数据,模拟测试行为等……多数情况下,这些开源框架都已经提供了。因为你遇到的问题,别人可能早已遇见过。这个世界上有很多聪明而又乐于分享的程序员,不要局限在自己公司一隅。睁大眼睛看看满世界吧。所谓“君子生非异也,善假于物也”。好程序员,也要这样。

  说不定,你会抛弃TDD,因为你找到了更好的适合你的做法。


  • 本文相关:
  • 站长必读,如何真正写好一篇原创文章
  • 互联网之路细嗅蔷薇 资深站长分享掘金之道
  • 企业站的站长的工作重心究竟有哪些
  • 浅析:行业门户网站的一些盈利模式
  • 3000IP的企业网站每天订单不到30个的苦恼
  • 网站想内外兼修?先学习提高网站可用性的6大原则
  • 浅谈网页设计中的简约之美
  • 网页改版实战:日本设计师如何彻底优化旅游网站?
  • 网页改版实战!日本设计师如何彻底优化招聘网站?
  • 2015年值得关注的21个网页设计趋势
  • 免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved