测试、Debug、软件改进(与猎手的谈话录)

发表于:2007-04-22来源:作者:点击数: 标签:测试猎手谈话录debug改进
今天晚上猎手在MSN上问我知不知道AQTime,我说我不知道。后来才知道这是一个 测试工具 。 猎手跟我说,准备学一学 软件测试 ,因为我们公司的软件真是BUG一大把,随便怎么玩都能玩出错误来~~~。 我对他说,你搞错了,你这种情况,需要的不是测试,而是

今天晚上猎手在MSN上问我知不知道AQTime,我说我不知道。后来才知道这是一个测试工具

猎手跟我说,准备学一学软件测试,因为我们公司的软件真是BUG一大把,随便怎么玩都能玩出错误来~~~。


我对他说,你搞错了,你这种情况,需要的不是测试,而是Debug。测试部门的职责,是经过系统的科学的严格的测试之后,告诉你软件中有哪些问题,这些问题产生的条件、环境甚至可能的原因是什么。但测试部门没有责任告诉你这个问题应该如何修改,组建一个测试部门并不能减少现有系统中的bug(相反可能因为测试更严格,有更多的bug会出现)。现在猎手的情况是,不需要测试部门,这些问题已经暴露出来了,那么还要等测试部门干什么呢?赶紧debug才是正事。当然,如果有测试部门的话,也许测试部门能更准确的告诉你问题出现的时机、条件、环境以及(如果测试部门足够好的话),还能告诉你可能的原因,可以减轻你debug的难度。

于是猎手就问我,那你有啥好建议么,偶们的代码BUG太多。而且更麻烦的是程序还不是自己写的,修改难度很大,有牵一发而动全身的郁闷~~。
其实,猎手的问题是现有软件开发中的典型场景。当然我也碰到过类似的情况:软件bug一堆,代码一塌糊涂,日程排得很紧,自己对这个还不熟。一般来说,看到这种代码首先想到的就是“还不如重写一套呢”。但是这个想法是不对的。因为一个软件产品,只要它是一个产品,哪怕它的核心功能再简单,整个软件产品也是复杂的,因为除了核心功能之外,一个产品往往附带了大量周边需求,而就是这些周边需求,导致了一个看似简单的软件系统复杂化。我们大多数程序员都有一种本领:透过现象看本质。这个本领其实是很不错的,在很多时候都能抓住要领,但是,在实际软件开发时,这种本领有时会带来不小的误导:只看到软件的核心功能而忽视了其复杂的周边功能,导致对结果的盲目乐观。其实,很多软件的核心功能实现可能只需要很短的时间(甚至一两天就可以了),但周边功能需要占用大量的时间。而且很多周边功能的需求会反过来破坏原本和谐的核心设计,使得原先漂亮的设计迅速变坏,代码迅速变乱,结果实际完成的时间将是你预计时间的好几倍甚至更多,而实际完成的效果并不一定比原先那个“bug多多”的版本要好,因为原先的代码是经过很多需求、例外、错误之后慢慢修补和积累起来的,有些情况你未必了解,在你的代码中也就未必能考虑进去,从而导致原先好不容易被修补好的问题在你的“新版本”中再次重现。

以我个人的经验来说,碰到这类情况,首先就是对已有的代码给予足够的尊重,无论它多么乱多么难看多么不顺眼,只要它能工作,就让它在那里工作。如果出现bug,需要修改了,再来修改。修改的时候,要注意控制范围:找到bug发生的一个足够小的范围,然后尽可能的把这个部分独立出来——所谓独立的意思,就是找到不影响其他部分的相对单独的一个部分,例如一个函数、一个类、一个线程,或者是其他的相对独立的单元。——然后保持这个单元的接口不变(无论这个接口在你看来有多不合理,不要动它),将其内部整理到你满意的程度,注意小步进行,经常运行或调试,保证除了这个bug之外的其他部分不变。这个过程是迭代的,下一次再碰到问题的时候,你就可以把你已经修改过的部分考虑在内进一步改进——因为你改过它,已经对它的代码熟悉了。这时,如果碰到你修改过的这个单元接口不合理,那么就改掉它,但同时要保证外在的部分尽可能的不受影响。这种小步骤的、逐步的、迭代改动,一方面将你确实需要修改的部分做了充分的改进,到达你所满意的程度;另一方面最大限度的保证了程序的原样性。对于猎手问的另外一种情况那要是开发新软件,你有什么好的建议伐(如何对大限度的减少BUG),道理也是类似的,首先保证新加的部分足够好,然后让软件能工作,然后当碰到bug的时候,按上面的方法改进。

当然,这也是一种半理想化的情况,因为先前的代码很有可能没有那么清晰(不过幸运的是我遇到过的大部分情况是比较清晰的——毕竟连面向过程的模块化都不遵守的程序现在已经不常见了),比如复杂的线程,或碰到某些程序包含关键的全局变量,这时没有那么容易做到在一处改动而不影响全局。这时我们需要做一点技巧性的工作——我比较常用的一种做法是对全局变量进行复制再复制——先将全局变量复制给一个局部变量进行局部处理,处理完成之后再复制回全局变量。这个做法会降低效率,但是在一定程度上它可以减少耦合度,让局部的改动变成可能。如果涉及线程操作,一般常见的思路是对线程代码进行串行化处理——也就是减少不必要的并发操作,降低复杂度,然后在串行的内部进行改进——最后再优化,那是很后面的事情了。

其实代码改进的方法有很多,但是我相信:做尽量小的改动,尽可能维持现有的代码正常运作,这是一个比较有帮助的总体思路。当然,在隔离范围之内,改动还是彻底一点比较好,运用重构和必要的测试保证(如果能写单元测试最好,如果不能写,普通测试也行),将代码修改到最合自己的口味——希望我自己的口味比较正常:) ,这样对下一步的动作是很有好处的。

原文转自:http://www.ltesting.net