循序渐进学习JUnit

2008-02-23 10:13:11来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

开发人员JUnit 循序渐进学习JUnit 作者:Michel Casabianca 使用最流行的开放资源测试框架之一学习单元测试基础。 使用JUnit可以大量减少Java代码中程序错误的个数,JUnit是一种流行的单元测试框架,用于在发布代码之前对其进行单元测试。现在让我们来详细研究如何使用诸如JUnit、Ant和Oracle9i JDeveloper等工具来编写和运行单元测试。 为什么使用JUnit? 多数开发人员都同意在发布代码之前应当对其进行测试,并利用工具进行回归(regression)测试。做这项工作的一个简单方法是在所有Java类中以main()方法实施测试。例如,假设使用ISO格式(这意味着有一个以这一格式作为参数的构造器和返回一个格式化的ISO字符串的toString()方法)以及一个GMT时区来编写一个Date的子类。清单1 就是这个类的一个简单实现。 不过,这种测试方法并不需要单元测试限定语(qualifier),原因如下: 在一个类中进行测试的最小单元是方法,你应当对每个方法进行单独测试,以准确地找出哪些方法工作正常,哪些方法工作不正常。 即使前面的测试失败,也应当对各个方法进行测试。在此实施中,如果单个测试失败,后面的测试将根本不会运行。这就意味着你不会知道不良代码在你的实施中所占的百分比。 测试代码会出现在生成的类中。这在类的大小方面可能不是什么问题,但却可能会成为安全性因素之一:例如,如果你的测试嵌入了数据库连接密码,那么这一信息将很容易用于已发布的类中。 没有框架可以自动启动这一测试,你必须编写一个脚本来启动每一个测试。 在编写一个报告时,你必须编写自己的实现,并定义规则,以方便地报告错误。 JUnit框架就是设计用来解决这些问题的。这一框架主要是所有测试实例(称为"TestCase")的一个父类,并提供工具来运行所编写的测试、生成报告及定义测试包(test suite)。 让我们为IsoDate类编写一个测试:这个IsoDateTest类类似于: import java.text.ParseException; import junit.Framework.TestCase; /** * Test case for IsoDate. */ public class IsoDateTest extends TestCase { public void testIsoDate() throws Exception { IsoDate epoch=new IsoDate( "1970-01-01 00:00:00 GMT"); assertEquals(0,epoch.getTime()); IsoDate eon=new IsoDate( "2001-09-09 01:46:40 GMT"); assertEquals( 1000000000L*1000,eon.getTime()); } public void testToString() throws ParseException { IsoDate epoch=new IsoDate(0); assertEquals("1970-01-01 00:00:00 GMT",epoch.toString()); IsoDate eon=new IsoDate( 1000000000L*1000); assertEquals("2001-09-09 01:46:40 GMT",eon.toString()); } } 本例中要注意的重点是已经编写了一个用于测试的独立类,因此可以对这些文件进行过滤,以避免将这一代码嵌入到将要发布的文档中。另外,本例还为你希望在你的代码中测试的每个方法编写了一个专用测试方法,因此你将确切地知道需要对哪些方法进行测试、哪些方法工作正常以及哪些方法工作不正常。如果在编写实施文档之前已经编写了该测试,你就可以利用它来衡量工作的进展情况。 安装并运行JUnit 要运行此示例测试实例,必须首先下载并安装JUnit。JUnit的最新版本可以在JUnit的网站 www.junit.org免费下载。该软件包很小(约400KB),但其中包括了源代码和文档。要安装此程序,应首先对该软件包进行解压缩(junitxxx.zip)。它将创建一个目录(junitxxx),在此目录下有文档(在doc目录中)、框架的应用编程接口(API)文档(在javadoc目录中)、运行程序的库文件(junit.jar)以及示例测试实例(在junit目录中)。截至我撰写本文时,JUnit的最新版本为3.8.1,我是在此版本上对示例进行测试的。 图1 运行IsoDate测试 要运行此测试实例,将源文件(IsoDate.java和IsoDateTest.java)拷贝到Junit的安装目录下,打开终端,进入该目录,然后输入以下命令行(如果你正在使用UNIX): export CLASSPATH=.:./junit.jar javac *.java 或者,如果你正在Windows,输入以下命令行 set CLASSPATH=.;junit.jar javac *.java 这些命令行对CLASSPATH进行设置,使其包含当前目录中的类和junit.jar库,并编译Java源文件。 要在终端上运行该测试,输入以下命令行: java junit.textui.TestRunner IsoDateTest 此命令行将运行该测试,并在图 1所示的控制台上显示测试结果。 才在此工具可以运行类名被传递到命令行中的单个测试。注意:只有对命令行的最后测试才在考虑之内,以前的测试都被忽略了。(看起来像一个程序错误,是吧?) JUnit还提供了利用AWT(抽象窗口工具包)或Swing运行测试的图形界面。为了利用此图形界面运行测试,在终端上输入以下命令行: java junit.awtui.TestRunner IsoDateTest 或者使用Swing界面: java junit.swingui.TestRunner IsoDateTest 此命令行将显示图 2所示的界面。要选择一个测试并使其运行,点击带有三个点的按钮。这将显示CLASSPATH(还有测试包,但我们将在后面讨论)中所有测试的列表。要运行测试,点击"Run"按钮。测试应当正确运行,并在图 2所示的界面中显示结果。 在此界面中你应当选中复选框"Reload Classes Every Run",以便运行器在运行测试类之前对它们进行重新加载。这样就可以方便地编辑、编译并运行测试,而不需要每次都启动图形界面。 在该复选框下面是一个进度条,在运行较大的测试包时,该进度条非常有用。运行的测试、错误和失败的数量都会在进度条下面显示出来。再下面是一个失败列表和一个测试层次结构。失败消息显示在底部。通过点击Test Hierarchy(测试层次结构)面板,然后再点击窗口右上角的"Run"按钮,即可运行单个测试方法。请记住,使用命令行工具是不可能做到这些的。 注意,当运行工具来启动测试类时,这些类必须存在于CLASSPATH中。但是如果测试类存储在jar文件中,那么即使这些jar文件存在于CLASSPATH中,JUnit也不能找到这些测试类。 图2 用于运行测试的Swing界面 这并不是一种启动测试的方便方法,但幸运的是,JUnit已经被集成到了其他工具(如Ant和Oracle9i JDeveloper)中,以帮助你开发测试并使测试能够自动运行。 编写Junit测试实例 你已经看到了测试类的源代码对IsoDate实施进行了询问。现在让我们来研究这样的测试文件的实施。 测试实例由junit.frameword.TestCase继承而来是为了利用JUnit框架的优点。这个类的名字就是在被测试类的名字上附加"Test"。因为你正在测试一个名为IsoDate的类,所以其测试类的名字就是IsoDateTest。为了访问除私有方法之外的所有方法,这个类通常与被测类在同一个包中。 注意,你必须为你希望测试的在类中定义的每个方法都编写一个方法。你要测试构造器或使用了ISO日期格式的方法,因此你将需要为以ISO格式的字符串作为参数的构造器和toString()方法编写一个测试方法。其命名方式与测试类的命名方式类似:在被测试方法(或构造器)前面附加"test"。 测试方法的主体通过验证assertion(断言)对被测方法进行询问。例如,在toString()实施的测试方法中,你希望确认该方法已经对时间的设定进行了很好的说明(对于UNIX系统来说,最初问世的时间为1970年1月1日的午夜)。要实施assertion,你可以使用Junit框架提供的assertion方法。这些方法在该框架的junit.framework.Assert类中被实施,并且可以在你的测试中被访问,这是因为Assert是TestCase的父类。这些方法可与Java中的关键字assert(是在J2EE 1.4中新出现的)相比。一些assertion方法可以检查原始类型(如布尔型、整型等)之间或对象之间是否相等(利用equals()方法检查两个对象是否相等)。其他assertion方法检查两个对象是否相同、一个对象是否为"空"或"非空",以及一个布尔值(通常由一个表达式生成)是"真"还是"假"。在表 1中对这些方法进行了总结。 对于那些采用浮点类型或双精度类型参数的assertion,存在一个第三种方法,即采用一个delta值作为参数进行比较。另外还要注意,assertEquals()和assertSame()方法一般不会产生相同的结果。(两个具有相同值的字符串可以不相同,因为它们是两个具有不同内存地址的不同对象。)因此,assertEquals()将会验证assertion的有效性,而assertSame()则不会。注意,对于表 1 中的每个assertion方法,你还有一种选择,就是引入另一个参数,如果assertion失败,该参数就会给出一条解释性消息。例如,assertEquals(int 期望值, int 实际值)就可以与一个诸如assertEquals(字符串消息,int期望值,int实际值)的消息一起使用。 当一个assertion失败时,该assertion方法会抛出一个AssertFailedError或ComparisonFailure。AssertionFailedError由java.lang.Error继承而来,因此你不必在测试方法的throws语句中对其进行声明。而ComparisonFailure由AssertionFailedError继承而来,因此你也不必对其进行声明。因为当一个assertion失败时会在测试方法中抛出一个错误,所以后面的assertion将不会继续运行。框架捕捉到这些错误并认定该测试已经失败后,就会打印出一条说明错误的消息。这个消息由assertion生成,并且被传递到assertion方法(如果有的话)。 现在将下面一行语句添加到testIsoDate()方法的末尾: assertEquals("This is a test",1,2); 现在编译并运行测试: $ javac *.java $ java junit.textui.TestRunner IsoDateTest .F. Time: 0,348 There was 1 failure: 1) testIsoDate(IsoDateTest)junit.framework .AssertionFailedError: This is a test expected:

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:简化Spring--Model层

下一篇:JSP/JDBC MySQL乱码问题~~~