重构—改善既有代码的设计4——构筑测试体系
编写优良的测试程序,可以极大提高编程速度,即使不进行重构也一样。
自测试代码的价值:
程序员花时间最多的是用来调试:修复错误通常是比较快的,找出错误却是恶梦一场;当修好一个错误,总会有另一个错误出现,而且要花很久的时间才可以注意得到,又要花费大把时间去寻找
类应该包含自己的测试代码。每个类都应该有一个测试函数,用来测试自己这个类
确保所有测试都完全自动化,让它们检查自己的测试结果:将所期望的输出放进测试代码中,然后做出一个比较,可避免人工检测
测试简单了,和编译一样简单,可以在每次编译之后都进行测试,可以大大提高生产性能(没有花费太多时间去调试,可以快速找到错误,而且可以轻易定位问题,就在最近提交的代码中)
拥有强大的侦错能力:构筑的类能够自我测试&&可频繁运行测试
一套测试:就是一个强大的bug侦测器,能够大大缩减查找bug所需的时间
编写测试程序,意味着要写很多额外的代码,除非你确切体验到这种方法对编程速度的提升,否则自我测试就显不出它的意义。
编写测试代码的时机:
最有用:在开始编程之前,当需要添加特性的时候,先写相应测试代码
编写测试代码,就是回答此功能需要做些什么
使你把注意力集中于接口而非实现
为工作安上一个明确的结束标志,一旦测试代码正常运行,工作就结束了
极限编程:频繁测试是其重要一环。极限编程者都是十分专注的测试者。它们希望尽可能快速开发软件,也知道测试可让他们尽可能快速地前进
重构:必须编写测试代码。
方式:
testing main:惯用手法。每个类都应该有一个用于测试的main()。这是一个合理的习惯,但可能不好操控。问题:很难轻松运行多个测试
建立一个独立类用于测试,并在一个框架中运行它,使测试工作更轻松
junit测试框架:
框架简单,却可以让你进行测试所需的所有事情
assert():扮演自动测试角色
建议:
频繁运行测试。每次编译,请将测试也考虑进去,每天至少执行每个测试一次
重构过程中,可以只运行少数几项测试,主要用来检查当下正在开发、整理的代码
测试机制可以运行,的确测试了它该测试的东西(断言的合理使用)
功能:捕捉失败&&捕捉错误异常。出现形式不同,排除过程也不同
很好用的图形界面
单元测试:
目的:提高程序员的生产率
高度局部化的东西,每个测试类都隶属于单一包
能够测试其他包的接口。除此以外,将假设其他包一切正常
重构时,更多地依赖单元测试
功能测试:
保证软件能正常运作。
从客户角度保证质量,并不关心程序员的生产力。
应该由一个喜欢寻找bug的独立团队开发,且应使用重量级工具、技术来帮助自己开发良好的功能测试
一般,尽可能将整个系统当作一个黑箱。只观察特定输入导致的数据变化
功能测试,往往以其他工具辅助进行
一旦发现错误:
1.修改代码,排除错误
2.添加一个单元测试,暴露bug
每当收到bug报告,都应先编写一个单元测试,使bug浮现出来。如果出现其他相关失败,编写更多的测试。用单元测试来盯住bug,并确保单元测试不会由漏网之鱼
添加更多测试:
观察类该做的所有事情,针对任何一项功能的任何一种可能失败的情况,进行测试。不仅仅是测试所有public函数
测试是一种风险驱动的行为。测试的目的是希望找出现在、未来可能出现的错误。=》不会去测试那些仅仅读、写一个字段的访问函数,太简单,不太可能出错
注意:撰写过多测试,结果往往测试量反而不够。
哪怕只做一点点测试,你也可从中受益
测试要诀:测试你最担心出错的部分,才能从测试工作中得到最大利益。
编写未臻完善的测试并运行,好过对完美测试的无尽等待
测试技巧:
1.寻找边界条件
考虑可能出错的边界条件,将测试火力集中在那儿
包括寻找特殊的、可能导致测试失败的情况。文件相关的:第一个字符、最后一个字符、空文件……
2.测试扮演“程序公敌”的角色。积极思考如何破坏代码。这种思考可以提高生产力。
3.检查预期的错误是否如期出现。例如,关闭文件流后,再次读取。
当事情被认为应该出错时,别忘记检查是否抛出了预期的异常。
4.测试类愈来愈多,可以生成另外一个类,专门包含由其他测试类所组成的测试套件,以便拥有一个主控的“测试类
任何测试都不能证明一个程序没有bug。但一旦重构,可以更好地理解整个程序,从而找出更多bug
不要因为测试无法不着所有bug就不写测试,测试的确可以捕捉大多数bug
测试可以提高编程速度。其目的都是保证你能够测试所有情况的一切组合
应该把测试集中在可能出错的地方。观察代码,看哪儿复杂?观察函数,看哪儿可能出错?
当测试数量到达一定程度之后,继续增加测试带来的效益会曾宪递减事态,而非持续递增;如果试图编写太多测试,可能会因工作量太大而气馁,最后什么也写不成;
面向对象的测试
继承、多态会让测试变得比较困难。=》将有很多组合需要测试
不总时测试所有可能组合。尽量测试每个类,可以大大减少各种组合所造成的风险。
“花合理的时间抓出大多数bug”,好过“穷尽一生抓出所有bug”
测试代码:
与产品代码之间的区别:可以放心复制、编辑测试代码
请构筑一个良好的bug检测器,并经常运行它,对任何开发工作都大有裨益,这是重构的前提。