英文原文名叫《A Dozen Ways to Get the Testing Bug in the New Year》,是Aaron在java.net上发现的文章。原文地址http://today.java.net/pub/a/today/2004/01/22/DozenWays.html,原文是在2004年1月22日发表的,按常理说一片早已经过了时效期的文章是没有多大用处的,但是Aaron却认为时至今日该文依然值得一阅。
以下内容为Aaron遵照原文转译而来,如有翻译错误和纰漏,欢迎留言批评斧正。
————————————————————————————————————————————————————————————————
测试驱动开发近来(2003-2004,Aaron注)越来越受到大家的关注,而且我相信大家对它的兴趣会越来越浓厚。一个重要的原因:大家都认识到测试是很重要的了。现在,有一些比较专业的开发人员在主张测试先行,他们认为这样可以帮助他们开发出更好的软件。这些人还发现测试驱动开发让他们的效率提高了很多,同时还减轻了很多压力。他们只需要很少的时间就可以生产出一整套可以运行通过的测试(脚本)和具有优良设计的代码。听起来不错吧?那就试一试吧,相信你一定会受益匪浅!
这篇文章列出了十二种关于怎样开始写测试和怎样坚持下去的实用方法,这些方法独立于你所使用的开发平台和开发方法。开始的两个技巧是教你怎样审视自己写出来的代码,这样你就不必一下子甩开代码去专注于测试,这样可能会比较容易让你接受我的观点;紧接着的两个方法将逐渐带你逐步了解测试驱动开发并感受它的的好处,而其余的8个技巧则是帮你更有效地完成你的测试。
让你的计算机去做那些让人厌烦的事情
从哪里开始我们的测试最简单呢?先把我们的期望列出来,接着用自动化的方式去验证它们,而不是靠眼睛去盯。我喜欢用计算机来完成这些事情,它比人工方式更可靠。当然,有的地方如果用计算机来摆平会很麻烦,这个时候还是我们亲自动手解决会比较划算,我们不要从这些比较麻烦的地方出发。先找个软柿子捏,捏得爽了自信也就有了,有了自信就好办事了。我发现在大多数情况下,main()这种形式的方法比较适合用于自动化测试,我不是说利用它作为程序入口点的特性,而是利用常见的方法中的Console形式来输出测试结果。
示例:假设你正在写一个java类,它只是实现一个简单的电子表格的功能,一个表格的单元格是用类似"A1"行号列号来唯一标识的,这个单元格用来存放一些数据,我们利用这个电子表格来实现数据的存储和查找等功能。这里有一个关于main()形式的测试用例:
Spreadsheet sheet = new Spreadsheet();
System.out.println("Cell reference:");
sheet.put("A1", "5");
sheet.put("A2", "=A1");
System.out.println("A2 = " + sheet.get("A2"));
System.out.println("\nCell change propagates:");
sheet.put("A1", "10");
System.out.println("A2 = " + sheet.get("A2"));
System.out.println("\nFormula calculation:");
sheet.put("A1", "5");
sheet.put("A2", "2");
sheet.put("B1", "=A1*(A1-A2)+A2/3");
System.out.println("B1 = " + sheet.get("B1"));
}
你或许已经发现这些代码似曾相识了吧~而我们这样做的动机很简单,我们只是想检验代码运行结果是否与期望相符。如果是,那我们就可以对自己的代码更加有信心了。这些年来这个方法一直是我的必备武器,它很简单,也很好用。需要提醒的是,这种方法可能会耗费自己大量的时间——一旦电子表格的相关代码发生了变动,我们就必须运行一次这样的测试来检验这些改动是否影响到代码结构。显然,如果这些测试运行的结果是下面这样,那我们就可以对自己的代码改动放心了。
Cell reference:
A2 = 5
Cell change propagates:
A2 = 10
Formula calculation:
B1 = 15
这种方法还是有一个弊端:需要我们每次在测试运行之后手工(人眼)检查测试结果。如果我们正在大规模使用这种方法,那么这样的劳作方式会让人们感到痛苦。最终我们会感到厌烦并可能因此就放弃了测试。人工检查结果还要求我们在测试运行的时候对于期望结果有个清晰地了解——如果这样的话,在你需要检查的结果由十几个点组成的时候……还记得住吗?如果我们记不住或者理不清,我们的测试就失去了意义.
JUnit,这个工具可以帮你解决这个问题。如果你从来没有用过它,没关系,JUnit FAQ(http://junit.sourceforge.net/doc/faq/faq.htm)可以帮上你的忙,让你快速处理测试。运用JUnit可以让计算机帮你运行这些测试并帮你验证测试结果。下面是在JUnit中运行的测试代码:
public class SpreadsheetTest extends TestCase {
public void testCellReference() {
Spreadsheet sheet = new Spreadsheet();
sheet.put("A1", "5");
sheet.put("A2", "=A1");
assertEquals("5", sheet.get("A2"));
}
public void testCellChangePropagates() {
Spreadsheet sheet = new Spreadsheet();
sheet.put("A1", "5");
sheet.put("A2", "=A1");
sheet.put("A1", "10");
assertEquals("10", sheet.get("A2"));
}
public void testFormulaCalculation() {
Spreadsheet sheet = new Spreadsheet();
sheet.put("A1", "5");
sheet.put("A2", "2");
sheet.put("B1", "=A1*(A1-A2)+A2/3");
assertEquals("15", sheet.get("B1"));
}
}
请注意:结果的验证已经用assertEquals()方法来处理了,这个方法帮你验证我们所期望的值是否和测试运行后的结果一致,这样就不需要你痛苦地记忆那些数据了。JUnit有图形界面和非图形界面两种,不管是哪一种都能为你提供简洁而精确的输出。如果使用非图形界面形式的,当所有的测试都通过(预期结果与实际运行结果一致)的时候你可看到:
Time: 0.04
OK (3 tests)
如果是使用具有图形界面的,这时候你会看到一个绿色的条:
如果期望与实际运行结果不符,JUnit会迅速作出反应,根据选用的JUnit形式不同,看到的可能是一个刺眼的失败信息或者一个红色的条,同时会提供关于测试详细信息。
自动化测试是多此一举?现在这个问题已经不需要再争论了吧。自动化测试组件可以带你脱离手工测试的苦海,这样你就有了更充足的时间去设计更加全面的测试。Pragmatic Unit Testing 这本书可以帮助你提升测试技巧,写出更好的测试。如果你新增加了一批测试用例,你也已不需要多花很多的时间来运行这些测试。随着项目中测试的逐渐深入,自动化测试的价值会逐渐提升——尤其是在进行回归测试的过程中,它可以节省很多时间。