AutoCAD 3DMAX C语言 Pro/E UG JAVA编程 PHP编程 Maya动画 Matlab应用 Android
Photoshop Word Excel flash VB编程 VC编程 Coreldraw SolidWorks A Designer Unity3D
 首页 > JAVA编程

谈谈如何保证测试代码的正确性

51自学网 2015-09-02 http://www.wanshiok.com

  OK,下面开始逐一讨论:

  一、TEST FIRST

  TEST FIRST其核心不在“TEST”,而是“FIRST”。测试驱动开发的目的是开发,而测试优先是其过程,从测试开始,以测试结束,中间是小增量的迭代开发,测试代码和开发代码同步进行(注:其实我认为测试代码也是开发代码,不过为了区分这2者,下面所说到的开发代码都指不包括测试代码部分;另外,这句话本身要是仔细扣是有问题的,并不全面,只是测试驱动开发的一个方面来说)。

  不是说要保证测试代码的正确性就要TEST FIRST,而是TEST FIRST可以最大限度地保证测试代码的正确性。(这里要说回代码本身,无论是测试代码还是开发代码,无非是一种开发语言的代码,要保证代码的正确性,我认为主要是良好的设计、清晰的结构以及语言的合理使用,其中保持代码结构清晰是最容易做到的,简单的缩进,统一的编码风格基本就算合格了。对于测试代码,也是一样的道理。)如果不先写测试,那么一定是有目标代码了(废话好像,^_^),这在写测试代码时必然会受到限制,别说自己可以不理会写好的目标代码(我想90%的人还是比较难做到的吧,都写好了,还改来改去,而且一个接口一旦公布出去,由不得你想改就改),写测试的时候,束手束脚,这不能动,那不能改,你怎么办,当然是按照自己写开发代码的思路来写测试了(呵呵,好像如果是先写了开发代码而后写测试代码,测试代码的目的也多是为了验证开发代码的正确性而已,至少以前我有这样做时是这样的状况),于是你的思维被禁锢了,测试代码的好坏(这里的坏不是说不能用,^_^),就由开发代码控制了。说回到我举的例子,如果开始的时候确实是因为时间关系、以及需求的突发性改变导致没有很细致地考虑如何修改设计(实际上我认为这样的状况实在太多,我接触的用户,有很确定地提出某个需求,而最后由于来看演示的用户完全否决该需求而导致完全推翻重来的,不过要说的是,我面对的用户实在没法子,人改需求的用户比当初提需求的用户官大,sweat,不改不行,时间还不能拖,原因就不细说了),直接就由开始的构思来写好目标代码先,那么会是什么样的局面?首先是时间已经花进去了(呵呵,单就这个例子当然其实也没多少时间的,是相对来说的),而到开发代码完成要写测试的时候必然导致写测试的目的是验证这段代码的正确与否,这样的话,不用说,准备好输入数据,验证输出数据嘛,哪还管那么多?(我认为是这样的,一旦确定了目标,而且还是那么实在的目标,做起事来的时候,脑袋里早就计划好了,就没有余地来考虑其它东西了,更不要说跨出这件事情考虑做另一件事情了)。而采用测试优先的方式的话,其结果和过程我在例子里面也写的很详细了,没有目标代码写测试其实是很轻松的事情,因为现在什么都还没有,自己想怎么就怎么样,想让测试代码清晰、简单明了,那还能复杂到那里去?

  所以说,TEST FIRST是提高测试代码正确性的一个很好的途径。这里先写开发代码后写测试代码的方式,其实说白了,是我想说的第三点,那样的测试,真的就很容易成了为了测试而测试的代码(记忆里,以前我这么做的时候,还都是为了测试而测试,呵呵,不知道其他朋友会如何)。

  二、只写出需求测试(注意,是目标代码需求,这个代码的用户当然是自己了,^_^)

  前面说到,要保证测试代码的正确性,从保证代码本身的清晰来入手,只写需求测试也是一个道理,不过要把它放在这里,主要也还是因为自己以前写测试的时候,往往都很容易忘记这一点,简单的比方说,当一个测试会牵涉多个类的时候,本来其实也很明确的,单元测试嘛,你测试你的目标类就OK了呗,但是每个人的认识不同,写测试的水平不同,更在于小心谨慎,总是在写目标类测试的时候,想这个地方虽然是A类做的事情,不过我也连带着加个断言看下A做对了没有吧,反正也就多加一句,以防万一嘛(太小心了,其实过界了都不知道)。别小看一行代码,我的感觉,超过20行(空行不算,sweat)的一个方法在你写完这个方法几个月后回来看,晕的几率很大,至少要立刻明白这个方法的内部结构是基本不能。积少成多,如果目标类关联了A、B、C、D、E五个甚至更多,多出来的恐怕就不是一、二行代码了,而且你一次小心一定会多次小心的,比如,在一个测试用例中(我所指的测试用例不是指TESTCASE类,而是该类中的一个TEST方法)你的断言中包含了对A的一个操作结果的测试,那么在另一个测试用例中,如果还用到A的这个操作,我肯定你还会写这个断言,因为你第一次的小心不是偶然。人说,小心使得万年船,不过,杞人忧天就过犹不及了。而写测试的时候,是很容易就会犯这样的错(唉,其实是我当初很容易犯这样的错,sweat)。

  所以,职责分明,只写你需要的你该做的测试,别一开始就怀疑这个怀疑那个,处处小心,步步为营。我们要如何使用我们的代码,那么就写那样的测试,现在我都是拿测试代码当例子,写了一个公共模块,除了有文档以外,都是建议要是觉得看文档麻烦就看测试代码如何使用,或者你干脆就拷贝我的测试代码过去改下用好了。

  三、不要为了测试而测试(这句话是和一个朋友聊天时他说是他和Kent Back交流时Kent Back提醒他的,这里我也不是很确定对这句话的理解的正确与否,因为理解一句话,上下文也是关键的,而我并不很了解我朋友同Kent Back谈话的具体内容和过程,不过这里还是作为一个要点谈谈自己的想法)

  不要为了测试而测试,^_^,虽然说要写测试,不过大家都是开发的,如果你的测试代码的目的成了测试,我还是会说你这就不够“专业”了。(这句有点耳熟,星星:拜托,虽然大家都是中国人,你要是抄我台词,照样要告你剽窃)

  其实开始我听朋友讲这句,似懂非懂,因为毕竟只是一句没头没尾的话,听过就算了。但是细细品了,确觉得有些道理(^_^,不知道我的想法是不是Kent Back说这句话的本意)。言归正传,开发人员的测试代码,还是不要以测试目标代码为目标的好,这样才能更好的确保测试代码的正确性。

  这里要澄清的是,不是说测试代码就不要去拿来测试了,呵呵,测试代码的结果(无论是否测试先行)其实都是一样的,都是可以自动(或者半自动,^_^)执行的测试,我说了,是写测试的目的,由于你的目的不同,中间的过程也是大不相同的,对于开发人员来说,开发代码是最终目的(什么需求分析、系统设计啦等等等等,还不是为了能满足用户需求的代码出来给用户跑嘛,哦,当然,最最终目的,咱还是靠这个养家糊口,sweat),所以开发过程中的单元测试代码,它只是辅助我们开发的(这点好像说了好多遍了,sweat,别丢我臭鸡蛋),所以这些测试代码的最终目的也是为了开发代码,它和测试人员写的测试代码有本质的区别(测试人员要是来看我这篇文章,嘿嘿,SORRY啦,真不是为测试写的)。

  说得好绕口,不过,只有清楚了这点,那么前面2点才会站得住脚,如果一开始得测试代码就只是为了测试目标代码得功能,那么TEST FIRST就是笑话了,连目标都没有怎么可能测试,而根据需求写测试,根本不管内部逻辑以及边界条件等等等等什么这覆盖那覆盖的测试,测试人员一定说我瞎掰了,这也能拿来测试?

  所以,要保证测试代码的正确性,别为了测试而测试,只有不为了测试而测试,你才会真正做到第一、二点。(好像这点应该摆第一去,sweat,不过既来之则安之落)

  四、每次写一点(原子级)测试

  这一点,其实说白了,是XP中的小增量迭代,测试驱动开发强调虽然是测试优先,但是测试开发并行过程中相互的交互作用是很关键的。

  一个很笼统的测试用例,既测试这个又测试那个,难免会复杂起来(量的积累带来质的变化),所以最好是一个测试用例只针对一个需求写测试,少量多次嘛。添加一点测试,运行失败,修改代码,运行测试,直到成功,然后重构代码,最后测试通过,是测试驱动开发的一个小迭代周期,当然迭代周期的控制是可以因人而异的。

  每次写的测试越少,那么表示你关注的问题或者需求就越少,那么精力就越集中,相对的测试的代码量也越少,大问题通常都可以通过将其拆分成多个小问题分别解决来得到解决,如果觉得测试代码太复杂,自然也可以通过拆分复杂的测试代码为多个小的相对简单的测试代码段来缩小其复杂性。

  五、Clean code that works,(当然包括测试代码啦,^_^)

  再好的设计,架不住你混乱的代码,最简单的,没有良好缩进格式的代码,看起来真是恶梦,现在工具这么好,都能自动化了,不过我还是能在工作中看到很多没有格式化好的代码,sigh,最起码的要求都做不到,更不要说让他去重构代码了,估计写测试也是没戏,TEST FIRST更是免谈了。

  所以别看这句话简单,做到了再体会才能理解。(这里又要唠叨下TEST FIRST,如前所述,我其实开始的时候是先写开发代码后写测试的,每次都很犹豫,是不是先写测试,而很多时候都架不住自己脑袋里那个已经“成型”的设计,要立刻写开发代码,测试嘛,总是认为后面再来也一样,不过在我多次逼自己先写测试之后,经历感觉完全不同,TEST FIRST根本不是原先自己所想的那样简单,所以,虽然我这里说了一堆它的好处,还是希望没有体验过的朋友别管那么多,试试先,一定会喜欢上它的,不过,要真正到处都使用它,还是要提高写测试代码水平的,因为测试代码不是那么简单哦,特别是和数据库和WEB搭上关系后)。

  好了,这一点,其实我也不用再详细说了,其实大家瞎子吃馄饨心里有数。至于怎么做到,嘿嘿,看看《测试驱动开发》吧,也许会有体会的,而我这里就超出范畴了,关于这点,我也就此打住,因为人大师的金玉良言更精辟更有说服力,我也不想在这里做转贴。

  六、对于一个应用框架,最好是针对这个框架先写一个测试框架(这其实是一个很具体的内容,不过现在JAVA在WEB方面用得很多,测试相对来说也比较难些,所以有这点)

  这一点是说到应用上了,^_^,其实也许这才是一些朋友希望交流的东西,因为开始我学写测试的时候,真不是我不想写啊,而是实在不知道怎么写,对一个复杂的应用,比如前端WEB,后台数据库,只要和这2者关联起来,再简单的需求写起测试来也是一大堆代码,有的甚至就根本不知道怎么测试。我现在也还是对数据库端的测试很惧怕,希望借此有朋友能讨论下。

  这里,我的观点是很明确的,一个应用框架,要有针对这个框架的测试框架来支持,才能让写出来的测试代码清晰以保证其正确性。测试框架的目的是要保证测试环境的正确搭建,这里说说题外话,测试环境,我认为是测试的关键,也是单元测试中最难的一个方面。对于一个接口的测试(纯粹的接口,不是实现类),可能就需要写一个扩展这个接口的实现类来辅助测试(我举的例子中,在开始考虑使用Decorator首先就是考虑的这个模式应用后的接口测试,这样我在测试具体的Decorator实现时才能抛开这个模式而单独测试实现类,而最后在column节点处理时用builder模式时,其实是拿默认的builder来做了这个测试的),这样做是我说的第二点;应用框架通常由接口架构,抽象出具体应用的接口,而具体应用则通过扩展该接口来实现功能,比如常见的WEB框架Structs,它的ACTION就是这样的接口,要做好对它的测试,框架本身的测试必不可少,但具体应用时其实和框架关系不大,只要制作符合要求的ACTION就可以了,这样的话,对ACTION的测试才是一般应用程序员要考虑的。对ACTION的测试,一种简单的方式是将ACTION中的要测试代码和STRUCTS框架隔离,这样就不需要访问STRUCTS框架,直接测试,但是,这样做其实不完整(不过这样做也有好处,就是将逻辑代码强制写在其它地方,而不写在ACTION中,当然ACTION本身就不是用来写这些的,不过我看到的,很多程序员其实是习惯于直接在ACTION中写业务逻辑代码的,这样的好处很少,就是眼前少写几行代码,但是以后就不见得了,这是我的第七点,大偷懒和小偷懒的区别),最好的状况是完整地测试ACTION类,从调用它开始,但是这样地话,准备测试环境会很麻烦,准备一个HTTP的REQUEST对象不是随便可以NEW出来的,会让人望而却步,要是只写一、二个这样的测试也就罢了,实际确实成百上千的,所以这个时候就需要一个测试框架了(呵呵,忽然想到,开始说测试框架,可能会有人误会,说是测试应用框架本身的代码,那样的测试当然不可少,不过这里说的测试框架,是指针对应用框架开发时要写的TESTCASE的框架),对于WEB测试有HTTPUNIT是不错的选择,不过还是比较低级的(呵呵,这里和语言一样的比喻,JAVA之类是高级语言,不是说它比汇编就高级),要针对STRUCTS框架写一个测试框架(现在有开源的这个框架),让测试环境的搭建工作简化到最少,这样真正写测试时才能只关注需求部分的测试代码,而测试代码本身也看起来不那么庞大。

  七、懒惰是程序员的通病,但是小偷懒就别了。(我有这样的观点:程序员的水平高低,其实从他偷懒的程度上是可以看出来的……^_^)

  第六点中已经带到了这个话题,其实只是调侃得说说自己的想法,不能算很正式的,因为这点其本质和测试没有一点关系。

  我很懒的,要不然不会选择这个职业,因为程序可以自动帮我做事,这样我就可以只是轻松地看着了,^_^。

  但是懒惰有很多种,有一种最直接的,只顾眼前的懒惰,还是拿不格式化代码来说,写的时候很随意,是好像让你少做了很多事,但是让看的人,让以后自己看的时候(特别是在查错时),付出更高的代价,我想,无论你在下次回头看这个代码的时候,理解它的时间是多花了多么少的时间,也比不上选个菜单自动格式化好代码花的时间少的。为变化而设计,正是为了将来能够更偷懒。有一个观点,现在我也想不起来是哪里看到了的,似乎哪里都有(《重构》啦、XP啦、测试驱动开发啦等等),就是在给一个已有模块添加新的功能前先重构已有代码,让已有代码适合于添加这个新的功能(好像第一次看到这个应该是《重构》一书),重构不是说让代码更好看而已,其目的就是为了适应变化,变化本来显得很大,但是可以通过重构来改善已有代码,让已有代码适合于新变化的添加,那么变化的复杂性就被缩小了。

  所以我说,小懒惰就免了,别影响大局,代码清晰、必要的重构,不作这些只是小懒惰,它们是以将来可能更大的付出为代价的。测试代码嘛,也只是代码而已。

  好了,终于写完了,中间几点难免有些雷同了(Clean code that works好像就可以涵盖所有了),还是总结下,思来想去,还是这句了:Clean code that works。大师的话就是精辟啊。不过,仔细想想,上面说的几点,好像都是没有说如何做到的,只是说了做到这几点可以极大得保证测试代码的正确性,但是这些关键点,要如何做到,我想大家还是多看看书吧,《测试驱动开发》、《重构》还有《极限编程-拥抱变化》这些都是难得的好书,我想,相对其它的书,这3本关键并不在于其有什么很高深的理论或者技术,而是它们都是指导程序员实践,如何从小处着手做程序,养成好的编程习惯,也更象是这些大师们自己手把手教咱写程序一样,难得的是文笔还很好,叙述得浅显易懂。

 
 
说明
:本教程来源互联网或网友上传或出版商,仅为学习研究或媒体推广,wanshiok.com不保证资料的完整性。
 

上一篇:从简单程序看java运行错误  下一篇:玩转Java的CLASSPATH