好消息是,测试驱动开发不仅仅适合于新代码。即使是程序员维护老代码时也可以利用它编写、运行以及通过测试。对于已经在生产中的遗留系统,测试确实更加 重要。只有通过测试,您才能确信您对于系统中的某一部分所做的改变不会中断其他地方的另外一部分。当然,您可能没有时间或者经费为一个规模庞大的代码基础达到 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);
}
}
}
}