测试还是调试?
如果遗留系统是一个好的系统,您的大多数测试还是很有希望通过的。尽管如此,您也会找到bug。当测试一个以前从未经过测试的代码基础时,这种情况很可能很快就出现而不是以后才出现。这时,标准的TDD方法是停止测试并且开始进行修改直到测试通过。然而,这是假设您已经测试了模型中的其他所有内容并且相当自信如果您的修改中断了系统中的其他部分,您可以立即发现。在遗留测试中,这不是一个安全的方法。在修改一个以前的bug时,您很有可能将一个新的bug引入未经测试的代码中;而且假如这样的话,您可能不能立即注意到这个新bug。因此,强烈建议首先编写更多的测试,稍后再对bug进行修复。归根结底,这是一种基于如下一些因素的判断调用:
bug是不是看起来简单、明显并且是局部的?
您是否理解bug出现的那段代码?
您是否理解所作的修复?
如果上面问题的回答都是“是”,那么就去修复这个bug。如果回答是“否”,则应该在修复改代码之前首先尽力扩充测试套件。
通过功能划分进行测试
在最高层次上开始测试能够以最快速度获得代码覆盖率。对于遗留代码,应该考虑应用程序在做什么而不是考虑单个方法。尽量为它所做的每件事编写测试。对于一个 GUI应用程序如jEdit,菜单项提供应用程序功能的一个好的典型。激活每个菜单并且证实它做了应该做的。例如,清单3展示了这样的测试,向一个窗口输入一些文本,激活“select all”菜单项,剪切所选中的文本,然后验证文本在剪贴板中,而不在窗口中。
清单3 测试jEdit菜单
public void testCut() {
JEditTextArea ta = view.getTextArea();
ta.setText("Testing 1 2 3");
JMenu edit = menubar.getMenu(1);
JMenuItem selectAll = edit.getItem(8);
selectAll.doClick();
JMenuItem cut = edit.getItem(3);
cut.doClick();
assertEquals("", ta.getText());
assertEquals("Testing 1 2 3", getClipboardText());
}
private static String getClipboardText() {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable contents = clipboard.getContents(null);
if (contents != null
&& contents.isDataFlavorSupported(DataFlavor.stringFlavor) ) {
try {
return (String) contents.getTransferData(DataFlavor.stringFlavor);
}
catch (Exception ex){
return null;
}
}
return null;
}
很可能我在这个方法中测试的有点太多。最好将一些测试转移到装备中。无论如何,尽一切办法进行测试。
我仍然在调查,但是清单3中的测试可能已经发现jEdit中的一个 bug。第一次我运行它时,测试失败了。当我设置了一个断点并且再次在调试器中运行它时,它通过了。这是一个Heisenbug!我怀疑这涉及到多线程的同步问题,所以我在调用setText()之前插入了一个2.5秒的时延,测试通过了。如果没有时延,测试始终失败。如果有时延,测试通过。下一步是找出为什么需要时延并且判断它是否是一个可修复的bug。没有人会说为GUI代码编写测试很简单。
