软件测试之遗留代码的测试方法

发表于:2009-03-23来源:作者:点击数: 标签:代码软件测试遗留
Test-first 编程是自 面向对象 编程以来最有效的编码方式,但它假定您从一个空白屏幕开始编程。当代码已经存在时,您应该怎么做呢?使用一个流行的开放源码的 Java 工具作为例子,作者 Elliotte Rusty Harold 向您展示了如何为从未测试过的遗留代码开发测试套
Test-first 编程是自面向对象编程以来最有效的编码方式,但它假定您从一个空白屏幕开始编程。当代码已经存在时,您应该怎么做呢?使用一个流行的开放源码的 Java™ 工具作为例子,作者 Elliotte Rusty Harold 向您展示了如何为从未测试过的遗留代码开发测试套件。
/* I have no idea how this works but it seems to.  Whatever you 
   do, don\'t touch this function, and don\'t break this code!  
 (虽然我不知道这段代码起什么作用,但是看上去它似乎是有用的。无论您做什么,一定不要碰这个函数,不要破坏这段代码!)
*/
    如果您曾经遇到过带有此类注释的代码,这种情况并不少见。因为没有人了解这些系统,所以有时候就使用规则约束禁止进入整个系统;但是仍然需要对这些系统进行维护。即使是一个已经完全没有 bug 的系统(又有哪个系统能够完全没有 bug?),外部环境的改变也会使代码的改变成为必要。Y2K 营业额是一个最大且最明显的例子。欧元的引入对于某些金融系统来说相当于造成外伤。Sarbanes-Oxley 引入了新的以前不存在的报告要求,而且为了支持这些新规则,必须对遗留软件进行翻新。这个世界不是静态的,所以软件也不能是静态的,它必须向前发展否则就会被代替。

  好消息是,测试驱动开发不仅仅适合于新代码。即使是程序员维护老代码时也可以利用它编写、运行以及通过测试。对于已经在生产中的遗留系统,测试确实更加 重要。只有通过测试,您才能确信您对于系统中的某一部分所做的改变不会中断其他地方的另外一部分。当然,您可能没有时间或者经费为一个规模庞大的代码基础达到 100% 的测试覆盖率,但是即使是并不完美的覆盖率也能减少失败的风险,加速开发并且产生更加健壮的代码。

  本文使用 jEdit 做为例子,向您展示如何为从未测试过的遗留代码开发一个单元测试套件。jEdit 是一个流行的开放源码的文本编辑器,它完全没有任何测试套件!但是我将马上开始对其进行修改。在本文中,我着手开发一个测试套件,其目的是为了使将来 jEdit 的开发更加多产、高效并且有趣。

第一次测试

  中国有句老话,千里之行始于足下。遗留代码的测试套件首先开始于一个单独的测试。重点是做什么和从何做起。不要掉入相信因为不能够测试每行代码所以就不能够测试任何东西的陷阱。只管打开您的 IDE 并且开始编写测试。使用 JUnit(或者 NUnit,或者 CppUnit,或者任何您喜欢的框架)和一个一般的 IDE,您通常就能够在 20 分钟以内编写出第一个测试。编写测试要比编写模型代码简单得多。测试很小并且具有独立的代码块。它们不需要很多配置、思考和理解。您不需要 “专业知识” 来艰难地编写出高质量的测试。

  测试套件需要做的第一件事情是直接到达方法的中心。寻找您能够做的最大范围、最全面的测试。对于一个独立的应用程序,可能是 main() 方法。例如,这是我的第一个 jEdit 测试用例。它所做的就是运行应用程序的 main() 方法并且检验它是否在屏幕上输出了正确的窗口:


清单 1. 测试 jEdit 的 main() 方法
import java.awt.Frame;
import junit.framework.TestCase;
public class MainTest extends TestCase {
    public void testMain() {
        org.gjt.sp.jedit.jEdit.main(new String[0]);
        // make sure there\'s a window on the screen
        Frame[] allFrames = Frame.getFrames();
        for (int i = 0; i < allFrames.length; i++) {
           Frame f = allFrames[i];
           if (f.isFocused()) {
              assertTrue(f instanceof org.gjt.sp.jedit.View);
           }
        }
    }
}


  第一个测试的目的不是在边界条件上费力,也不是为了查看解决了什么问题。第一个测试是一个发烟试验,目的是为了对于什么可能是错误的给您一个清晰的概念。即使最基本的测试也不能揭示出构造系统、运行时环境、已安装的软件以及对每件事情进行本质上的破坏的其他主要问题中存在的问题。我的第一个测试用例确实能够准确地发现 jEdit 代码基础的这样一个问题:在我的类路径中没有包含所有可能的目录。

  我并没有开始测试类路径配置,但是我寻找到的问题也是重要的,因为它可能导致代码基础很难调试。类似这种的全面测试涉及到应用程序的很多方面。很多不同的东西能够中断并且导致测试失败。就这种意义上说,并不是非常统一。在 test-first 编程中,这不是一个问题;但是当测试遗留代码时,您没有时间或者预算为每个单独的方法或者分支编写独立的测试。您必须在编写每个测试时尽量地覆盖尽可能多的方法和分支。使用一些测试来测试大部分代码比根本不进行测试要好。

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