﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-轻松-随笔分类-Junit相关</title><link>http://www.blogjava.net/relax/category/245.html</link><description>记述我学习java的里程</description><language>zh-cn</language><lastBuildDate>Wed, 07 Mar 2007 22:23:46 GMT</lastBuildDate><pubDate>Wed, 07 Mar 2007 22:23:46 GMT</pubDate><ttl>60</ttl><item><title>单元测试利器 JUnit 4</title><link>http://www.blogjava.net/relax/archive/2007/03/05/101945.html</link><dc:creator>轻松</dc:creator><author>轻松</author><pubDate>Mon, 05 Mar 2007 08:15:00 GMT</pubDate><guid>http://www.blogjava.net/relax/archive/2007/03/05/101945.html</guid><wfw:comment>http://www.blogjava.net/relax/comments/101945.html</wfw:comment><comments>http://www.blogjava.net/relax/archive/2007/03/05/101945.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/relax/comments/commentRss/101945.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/relax/services/trackbacks/101945.html</trackback:ping><description><![CDATA[
		<p>本文主要介绍了如何使用 JUnit 4 提供的各种功能开展有效的单元测试，并通过一个实例演示了如何使用 Ant 执行自动化的单元测试。本文假设读者对 Eclipse 下进行 Java 开发有一定的经验，并了解 Java 5 中的注解（annotation）特性。</p>
		<!--START RESERVED FOR FUTURE USE INCLUDE FILES-->
		<!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
		<!--END RESERVED FOR FUTURE USE INCLUDE FILES-->
		<p>
				<a name="N10053">
						<span class="atitle">引言</span>
				</a>
		</p>
		<p>毋庸置疑，程序员要对自己编写的代码负责，您不仅要保证它能通过编译，正常地运行，而且要满足需求和设计预期的效果。单元测试正是验证代码行为是否满足预期的有效手段之一。但不可否认，做测试是件很枯燥无趣的事情，而一遍又一遍的测试则更是让人生畏的工作。幸运的是，单元测试工具 JUnit 使这一切变得简单艺术起来。</p>
		<p>JUnit 是 Java 社区中知名度最高的单元测试工具。它诞生于 1997 年，由 Erich Gamma 和 Kent Beck 共同开发完成。其中 Erich Gamma 是经典著作《设计模式：可复用面向对象软件的基础》一书的作者之一，并在 Eclipse 中有很大的贡献；Kent Beck 则是一位极限编程（XP）方面的专家和先驱。</p>
		<p>麻雀虽小，五脏俱全。JUnit 设计的非常小巧，但是功能却非常强大。Martin Fowler 如此评价 JUnit：在软件开发领域，从来就没有如此少的代码起到了如此重要的作用。它大大简化了开发人员执行单元测试的难度，特别是 JUnit 4 使用 Java 5 中的注解（annotation）使测试变得更加简单。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="major2">
						<span class="atitle">JUnit 4 初体验</span>
				</a>
		</p>
		<p>在开始体验 JUnit 4 之前，我们需要以下软件的支持：</p>
		<ul>
				<li>Eclipse：最为流行的 IDE，它全面集成了 JUnit，并从版本 3.2 开始支持 JUnit 4。当然 JUnit 并不依赖于任何 IDE。您可以从 <a href="http://www.eclipse.org/"><font color="#996699">http://www.eclipse.org/</font></a> 上下载最新的 Eclipse 版本。 
</li>
				<li>Ant：基于 Java 的开源构建工具，您可以在 <a href="http://ant.apache.org/"><font color="#5c81a7">http://ant.apache.org/</font></a> 上得到最新的版本和丰富的文档。Eclipse 中已经集成了 Ant，但是在撰写本文时，Eclipse 使用的 Ant 版本较低（必需 1.7 或者以上版本），不能很好的支持 JUnit 4。 
</li>
				<li>JUnit：它的官方网站是 <a href="http://www.junit.org/"><font color="#5c81a7">http://www.junit.org/</font></a>。您可以从上面获取关于 JUnit 的最新消息。如果您和本文一样在 Eclipse 中使用 JUnit，就不必再下载了。 </li>
		</ul>
		<p>首先为我们的体验新建一个 Java 工程 —— coolJUnit。现在需要做的是，打开项目 coolJUnit 的属性页 -&gt; 选择“Java Build Path”子选项 -&gt; 点选“Add Library…”按钮 -&gt; 在弹出的“Add Library”对话框中选择 JUnit（<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#figure001"><font color="#5c81a7">图1</font></a>），并在下一页中选择版本 4.1 后点击“Finish”按钮。这样便把 JUnit 引入到当前项目库中了。</p>
		<br />
		<a name="figure001">
				<b>图1 为项目添加 JUnit 库</b>
		</a>
		<br />
		<img alt="图1 为项目添加 JUnit 库" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure001.jpg" />
		<br />
		<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0">
				<tbody>
						<tr>
								<td width="10">
										<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" />
								</td>
								<td>
										<table cellspacing="0" cellpadding="5" width="100%" border="1">
												<tbody>
														<tr>
																<td bgcolor="#eeeeee">
																		<a name="N1009A">
																				<b>请注意 JDK 的版本</b>
																		</a>
																		<br />
																		<p>JUnit 4.1 是基于 Java 5 的升级版本，它使用了 Tiger 中的很多新特性来简化原有的使用方式。正因为如此，它并不能直接运行在 JDK1.4.x 版本上。如果您需要在 JDK1.4.x 版本使用 JUnit 的话，请使用 3.8.1 版本。</p>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<p>可以开始编写单元测试了吗？等等……，您打算把单元测试代码放在什么地方呢？把它和被测试代码混在一起，这显然会照成混乱，因为单元测试代码是不会出现在最终产品中的。建议您分别为单元测试代码与被测试代码创建单独的目录，并保证测试代码和被测试代码使用相同的包名。这样既保证了代码的分离，同时还保证了查找的方便。遵照这条原则，我们在项目 coolJUnit 根目录下添加一个新目录 testsrc，并把它加入到项目源代码目录中（加入方式见 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#figure002"><font color="#5c81a7">图2</font></a>）。</p>
		<br />
		<a name="figure002">
				<b>图2 修改项目源代码目录</b>
		</a>
		<br />
		<img alt="图2 修改项目源代码目录" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure002.jpg" />
		<br />
		<p>现在我们得到了一条 JUnit 的最佳实践：单元测试代码和被测试代码使用一样的包，不同的目录。</p>
		<p>一切准备就绪，一起开始体验如何使用 JUnit 进行单元测试吧。下面的例子来自笔者的开发实践：工具类 WordDealUtil 中的静态方法 wordFormat4DB 是专用于处理 Java 对象名称向数据库表名转换的方法（您可以在代码注释中可以得到更多详细的内容）。下面是第一次编码完成后大致情形：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">package com.ai92.cooljunit;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 对名称、地址等字符串格式的内容进行格式检查
 * 或者格式化的工具类
 * 
 * @author Ai92
 */
public class WordDealUtil {

	/**
	 * 将Java对象名称（每个单词的头字母大写）按照
	 * 数据库命名的习惯进行格式化
	 * 格式化后的数据为小写字母，并且使用下划线分割命名单词
	 * 
	 * 例如：employeeInfo 经过格式化之后变为 employee_info
	 * 
	 * @param name	Java对象名称
	 */
	public static String wordFormat4DB(String name){
		Pattern p = Pattern.compile("[A-Z]");
		Matcher m = p.matcher(name);
		StringBuffer sb = new StringBuffer();
		
		while(m.find()){
			m.appendReplacement(sb, "_"+m.group());
		}
		return m.appendTail(sb).toString().toLowerCase();
	}
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>它是否能按照预期的效果执行呢？尝试为它编写 JUnit 单元测试代码如下：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">package com.ai92.cooljunit;

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class TestWordDealUtil {
	//测试wordFormat4DB正常运行的情况
	@Test public void wordFormat4DBNormal(){
		String target = "employeeInfo";
		String result = WordDealUtil.wordFormat4DB(target);
		
		assertEquals("employee_info", result);
	}
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>很普通的一个类嘛！测试类 TestWordDealUtil 之所以使用“Test”开头，完全是为了更好的区分测试类与被测试类。测试方法 wordFormat4DBNormal 调用执行被测试方法 WordDealUtil.wordFormat4DB，以判断运行结果是否达到设计预期的效果。需要注意的是，测试方法 wordFormat4DBNormal 需要按照一定的规范书写：</p>
		<ol>
				<li>测试方法必须使用注解 org.junit.Test 修饰。 
</li>
				<li>测试方法必须使用 public void 修饰，而且不能带有任何参数。 </li>
		</ol>
		<p>测试方法中要处理的字符串为“employeeInfo”，按照设计目的，处理后的结果应该为“employee_info”。assertEquals 是由 JUnit 提供的一系列判断测试结果是否正确的静态断言方法（位于类 org.junit.Assert 中）之一，我们使用它将执行结果 result 和预期值“employee_info”进行比较，来判断测试是否成功。</p>
		<p>看看运行结果如何。在测试类上点击右键，在弹出菜单中选择 Run As JUnit Test。运行结果如<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#figure003"><font color="#5c81a7">下图</font></a>所示：</p>
		<br />
		<a name="figure003">
				<b>图3 JUnit 运行成功界面</b>
		</a>
		<br />
		<img alt="图3 JUnit 运行成功界面" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure003.jpg" />
		<br />
		<p>绿色的进度条提示我们，测试运行通过了。但现在就宣布代码通过了单元测试还为时过早。记住：您的单元测试代码不是用来证明您是对的，而是为了证明您没有错。因此单元测试的范围要全面，比如对边界值、正常值、错误值得测试；对代码可能出现的问题要全面预测，而这也正是需求分析、详细设计环节中要考虑的。显然，我们的测试才刚刚开始，继续补充一些对特殊情况的测试：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">public class TestWordDealUtil {
	……
	//测试 null 时的处理情况
	@Test public void wordFormat4DBNull(){
		String target = null;
		String result = WordDealUtil.wordFormat4DB(target);
		
		assertNull(result);
	}
	
	//测试空字符串的处理情况
	@Test public void wordFormat4DBEmpty(){
		String target = "";
		String result = WordDealUtil.wordFormat4DB(target);
		
		assertEquals("", result);
	}

	//测试当首字母大写时的情况
	@Test public void wordFormat4DBegin(){
		String target = "EmployeeInfo";
		String result = WordDealUtil.wordFormat4DB(target);
		
		assertEquals("employee_info", result);
	}
	
	//测试当尾字母为大写时的情况
	@Test public void wordFormat4DBEnd(){
		String target = "employeeInfoA";
		String result = WordDealUtil.wordFormat4DB(target);
		
		assertEquals("employee_info_a", result);
	}
	
	//测试多个相连字母大写时的情况
	@Test public void wordFormat4DBTogether(){
		String target = "employeeAInfo";
		String result = WordDealUtil.wordFormat4DB(target);
		
		assertEquals("employee_a_info", result);
	}
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>再次运行测试。很遗憾，JUnit 运行界面提示我们有两个测试情况未通过测试（<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#figure004"><font color="#5c81a7">图4</font></a>）——当首字母大写时得到的处理结果与预期的有偏差，造成测试失败（failure）；而当测试对 null 的处理结果时，则直接抛出了异常——测试错误（error）。显然，被测试代码中并没有对首字母大写和 null 这两种特殊情况进行处理，修改如下：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">//修改后的方法wordFormat4DB
/**
	 * 将Java对象名称（每个单词的头字母大写）按照
	 * 数据库命名的习惯进行格式化
	 * 格式化后的数据为小写字母，并且使用下划线分割命名单词
	 * 如果参数name为null，则返回null
	 * 
	 * 例如：employeeInfo 经过格式化之后变为 employee_info
	 * 
	 * @param name Java对象名称
	 */
	public static String wordFormat4DB(String name){
		
		if(name == null){
			return null;
		}
		
		Pattern p = Pattern.compile("[A-Z]");
		Matcher m = p.matcher(name);
		StringBuffer sb = new StringBuffer();
		
		while(m.find()){
			if(m.start() != 0)
				m.appendReplacement(sb, ("_"+m.group()).toLowerCase());
		}
		return m.appendTail(sb).toString().toLowerCase();
	}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<a name="figure004">
				<b>图4 JUnit 运行失败界面</b>
		</a>
		<br />
		<img alt="图4 JUnit 运行失败界面" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure004.jpg" />
		<br />
		<p>JUnit 将测试失败的情况分为两种：failure 和 error。Failure 一般由单元测试使用的断言方法判断失败引起，它表示在测试点发现了问题；而 error 则是由代码异常引起，这是测试目的之外的发现，它可能产生于测试代码本身的错误（测试代码也是代码，同样无法保证完全没有缺陷），也可能是被测试代码中的一个隐藏的bug。</p>
		<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0">
				<tbody>
						<tr>
								<td width="10">
										<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" />
								</td>
								<td>
										<table cellspacing="0" cellpadding="5" width="100%" border="1">
												<tbody>
														<tr>
																<td bgcolor="#eeeeee">
																		<a name="N1010C">
																				<b>请牢记！</b>
																		</a>
																		<br />
																		<p>请牢记这一条 JUnit 最佳实践：测试任何可能的错误。单元测试不是用来证明您是对的，而是为了证明您没有错。</p>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<p>啊哈，再次运行测试，绿条又重现眼前。通过对 WordDealUtil.wordFormat4DB 比较全面的单元测试，现在的代码已经比较稳定，可以作为 API 的一部分提供给其它模块使用了。</p>
		<p>不知不觉中我们已经使用 JUnit 漂亮的完成了一次单元测试。可以体会到 JUnit 是多么轻量级，多么简单，根本不需要花心思去研究，这就可以把更多的注意力放在更有意义的事情上——编写完整全面的单元测试。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N1011B">
						<span class="atitle">JUnit 深入</span>
				</a>
		</p>
		<p>当然，JUnit 提供的功能决不仅仅如此简单，在接下来的内容中，我们会看到 JUnit 中很多有用的特性，掌握它们对您灵活的编写单元测试代码非常有帮助。</p>
		<p>
				<a name="N10125">
						<span class="smalltitle">
								<strong>
										<font face="Arial">Fixture</font>
								</strong>
						</span>
				</a>
		</p>
		<p>何谓 Fixture？它是指在执行一个或者多个测试方法时需要的一系列公共资源或者数据，例如测试环境，测试数据等等。在编写单元测试的过程中，您会发现在大部分的测试方法在进行真正的测试之前都需要做大量的铺垫——为设计准备 Fixture 而忙碌。这些铺垫过程占据的代码往往比真正测试的代码多得多，而且这个比率随着测试的复杂程度的增加而递增。当多个测试方法都需要做同样的铺垫时，重复代码的“坏味道”便在测试代码中弥漫开来。这股“坏味道”会弄脏您的代码，还会因为疏忽造成错误，应该使用一些手段来根除它。</p>
		<p>JUnit 专门提供了设置公共 Fixture 的方法，同一测试类中的所有测试方法都可以共用它来初始化 Fixture 和注销 Fixture。和编写 JUnit 测试方法一样，公共 Fixture 的设置也很简单，您只需要：</p>
		<ol>
				<li>使用注解 org,junit.Before 修饰用于初始化 Fixture 的方法。 
</li>
				<li>使用注解 org.junit.After 修饰用于注销 Fixture 的方法。 
</li>
				<li>保证这两种方法都使用 public void 修饰，而且不能带有任何参数。 </li>
		</ol>
		<p>遵循上面的三条原则，编写出的代码大体是这个样子：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">//初始化Fixture方法
@Before public void init(){……}

//注销Fixture方法
@After public void destroy(){……}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这样，在每一个测试方法执行之前，JUnit 会保证 init 方法已经提前初始化测试环境，而当此测试方法执行完毕之后，JUnit 又会调用 destroy 方法注销测试环境。注意是每一个测试方法的执行都会触发对公共 Fixture 的设置，也就是说使用注解 Before 或者 After 修饰的公共 Fixture 设置方法是方法级别的（<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#figure005"><font color="#5c81a7">图5</font></a>）。这样便可以保证各个独立的测试之间互不干扰，以免其它测试代码修改测试环境或者测试数据影响到其它测试代码的准确性。</p>
		<br />
		<a name="figure005">
				<b>图5 方法级别 Fixture 执行示意图</b>
		</a>
		<br />
		<img alt="图5 方法级别 Fixture 执行示意图" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure005.gif" />
		<br />
		<p>可是，这种 Fixture 设置方式还是引来了批评，因为它效率低下，特别是在设置 Fixture 非常耗时的情况下（例如设置数据库链接）。而且对于不会发生变化的测试环境或者测试数据来说，是不会影响到测试方法的执行结果的，也就没有必要针对每一个测试方法重新设置一次 Fixture。因此在 JUnit 4 中引入了类级别的 Fixture 设置方法，编写规范如下：</p>
		<ol>
				<li>使用注解 org,junit.BeforeClass 修饰用于初始化 Fixture 的方法。 
</li>
				<li>使用注解 org.junit.AfterClass 修饰用于注销 Fixture 的方法。 
</li>
				<li>保证这两种方法都使用 public static void 修饰，而且不能带有任何参数。 </li>
		</ol>
		<p>类级别的 Fixture 仅会在测试类中所有测试方法执行之前执行初始化，并在全部测试方法测试完毕之后执行注销方法（<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#figure006"><font color="#5c81a7">图6</font></a>）。代码范本如下：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">//类级别Fixture初始化方法
@BeforeClass public static void dbInit(){……}
	
//类级别Fixture注销方法
	@AfterClass public static void dbClose(){……}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<a name="figure006">
				<b>图6 类级别 Fixture 执行示意图</b>
		</a>
		<br />
		<img alt="图6 类级别 Fixture 执行示意图" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure006.gif" />
		<br />
		<p>
				<a name="N1017E">
						<span class="smalltitle">
								<strong>
										<font face="Arial">异常以及时间测试</font>
								</strong>
						</span>
				</a>
		</p>
		<p>注解 org.junit.Test 中有两个非常有用的参数：expected 和 timeout。参数 expected 代表测试方法期望抛出指定的异常，如果运行测试并没有抛出这个异常，则 JUnit 会认为这个测试没有通过。这为验证被测试方法在错误的情况下是否会抛出预定的异常提供了便利。举例来说，方法 supportDBChecker 用于检查用户使用的数据库版本是否在系统的支持的范围之内，如果用户使用了不被支持的数据库版本，则会抛出运行时异常 UnsupportedDBVersionException。测试方法 supportDBChecker 在数据库版本不支持时是否会抛出指定异常的单元测试方法大体如下：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">@Test(expected=UnsupportedDBVersionException.class)
	public void unsupportedDBCheck(){
		……
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>注解 org.junit.Test 的另一个参数 timeout，指定被测试方法被允许运行的最长时间应该是多少，如果测试方法运行时间超过了指定的毫秒数，则JUnit认为测试失败。这个参数对于性能测试有一定的帮助。例如，如果解析一份自定义的 XML 文档花费了多于 1 秒的时间，就需要重新考虑 XML 结构的设计，那单元测试方法可以这样来写：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">@Test(timeout=1000)
	public void selfXMLReader(){
		……
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a name="N10193">
						<span class="smalltitle">
								<strong>
										<font face="Arial">忽略测试方法</font>
								</strong>
						</span>
				</a>
		</p>
		<p>JUnit 提供注解 org.junit.Ignore 用于暂时忽略某个测试方法，因为有时候由于测试环境受限，并不能保证每一个测试方法都能正确运行。例如下面的代码便表示由于没有了数据库链接，提示 JUnit 忽略测试方法 unsupportedDBCheck：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">@ Ignore(“db is down”)
@Test(expected=UnsupportedDBVersionException.class)
	public void unsupportedDBCheck(){
		……
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>但是一定要小心。注解 org.junit.Ignore 只能用于暂时的忽略测试，如果需要永远忽略这些测试，一定要确认被测试代码不再需要这些测试方法，以免忽略必要的测试点。</p>
		<p>
				<a name="N101A4">
						<span class="smalltitle">
								<strong>
										<font face="Arial">测试运行器</font>
								</strong>
						</span>
				</a>
		</p>
		<p>又一个新概念出现了——测试运行器，JUnit 中所有的测试方法都是由它负责执行的。JUnit 为单元测试提供了默认的测试运行器，但 JUnit 并没有限制您必须使用默认的运行器。相反，您不仅可以定制自己的运行器（所有的运行器都继承自 org.junit.runner.Runner），而且还可以为每一个测试类指定使用某个具体的运行器。指定方法也很简单，使用注解 org.junit.runner.RunWith 在测试类上显式的声明要使用的运行器即可：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">@RunWith(CustomTestRunner.class)
public class TestWordDealUtil {
……
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>显而易见，如果测试类没有显式的声明使用哪一个测试运行器，JUnit 会启动默认的测试运行器执行测试类（比如上面提及的单元测试代码）。一般情况下，默认测试运行器可以应对绝大多数的单元测试要求；当使用 JUnit 提供的一些高级特性（例如即将介绍的两个特性）或者针对特殊需求定制 JUnit 测试方式时，显式的声明测试运行器就必不可少了。</p>
		<p>
				<a name="N101B5">
						<span class="smalltitle">
								<strong>
										<font face="Arial">测试套件</font>
								</strong>
						</span>
				</a>
		</p>
		<p>在实际项目中，随着项目进度的开展，单元测试类会越来越多，可是直到现在我们还只会一个一个的单独运行测试类，这在实际项目实践中肯定是不可行的。为了解决这个问题，JUnit 提供了一种批量运行测试类的方法，叫做测试套件。这样，每次需要验证系统功能正确性时，只执行一个或几个测试套件便可以了。测试套件的写法非常简单，您只需要遵循以下规则：</p>
		<ol>
				<li>创建一个空类作为测试套件的入口。 
</li>
				<li>使用注解 org.junit.runner.RunWith 和 org.junit.runners.Suite.SuiteClasses 修饰这个空类。 
</li>
				<li>将 org.junit.runners.Suite 作为参数传入注解 RunWith，以提示 JUnit 为此类使用套件运行器执行。 
</li>
				<li>将需要放入此测试套件的测试类组成数组作为注解 SuiteClasses 的参数。 
</li>
				<li>保证这个空类使用 public 修饰，而且存在公开的不带有任何参数的构造函数。 </li>
		</ol>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">package com.ai92.cooljunit;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
……

/**
 * 批量测试 工具包 中测试类
 * @author Ai92
 */
@RunWith(Suite.class)
@Suite.SuiteClasses({TestWordDealUtil.class})
public class RunAllUtilTestsSuite {
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>上例代码中，我们将前文提到的测试类 TestWordDealUtil 放入了测试套件 RunAllUtilTestsSuite 中，在 Eclipse 中运行测试套件，可以看到测试类 TestWordDealUtil 被调用执行了。测试套件中不仅可以包含基本的测试类，而且可以包含其它的测试套件，这样可以很方便的分层管理不同模块的单元测试代码。但是，您一定要保证测试套件之间没有循环包含关系，否则无尽的循环就会出现在您的面前……。</p>
		<p>
				<a name="N101D8">
						<span class="smalltitle">
								<strong>
										<font face="Arial">参数化测试</font>
								</strong>
						</span>
				</a>
		</p>
		<p>回顾一下我们在小节“<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#major2"><font color="#5c81a7">JUnit 初体验</font></a>”中举的实例。为了保证单元测试的严谨性，我们模拟了不同类型的字符串来测试方法的处理能力，为此我们编写大量的单元测试方法。可是这些测试方法都是大同小异：代码结构都是相同的，不同的仅仅是测试数据和期望值。有没有更好的方法将测试方法中相同的代码结构提取出来，提高代码的重用度，减少复制粘贴代码的烦恼？在以前的 JUnit 版本上，并没有好的解决方法，而现在您可以使用 JUnit 提供的参数化测试方式应对这个问题。</p>
		<p>参数化测试的编写稍微有点麻烦（当然这是相对于 JUnit 中其它特性而言）：</p>
		<ol>
				<li>为准备使用参数化测试的测试类指定特殊的运行器 org.junit.runners.Parameterized。 
</li>
				<li>为测试类声明几个变量，分别用于存放期望值和测试所用数据。 
</li>
				<li>为测试类声明一个使用注解 org.junit.runners.Parameterized.Parameters 修饰的，返回值为 java.util.Collection 的公共静态方法，并在此方法中初始化所有需要测试的参数对。 
</li>
				<li>为测试类声明一个带有参数的公共构造函数，并在其中为第二个环节中声明的几个变量赋值。 
</li>
				<li>编写测试方法，使用定义的变量作为参数进行测试。 </li>
		</ol>
		<p>我们按照这个标准，重新改造一番我们的单元测试代码：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">package com.ai92.cooljunit;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class TestWordDealUtilWithParam {

		private String expected;
	
		private String target;
	
		@Parameters
		public static Collection words(){
	    		return Arrays.asList(new Object[][]{
	          	{"employee_info", "employeeInfo"},		//测试一般的处理情况
	          	{null, null},							//测试 null 时的处理情况
	          	{"", ""},								//测试空字符串时的处理情况
	          	{"employee_info", "EmployeeInfo"},		//测试当首字母大写时的情况
	          	{"employee_info_a", "employeeInfoA"},	//测试当尾字母为大写时的情况
	          	{"employee_a_info", "employeeAInfo"}	//测试多个相连字母大写时的情况
	    		});
		}
	
	 	/**
	 	* 参数化测试必须的构造函数
	 	* @param expected	期望的测试结果，对应参数集中的第一个参数
	 	* @param target	测试数据，对应参数集中的第二个参数
	 	*/
		public TestWordDealUtilWithParam(String expected , String target){
			this.expected = expected;
			this.target = target;
		}
	
	 	/**
	 	* 测试将 Java 对象名称到数据库名称的转换
	 	*/
		@Test public void wordFormat4DB(){
			assertEquals(expected, WordDealUtil.wordFormat4DB(target));
		}
}
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>很明显，代码瘦身了。在静态方法 words 中，我们使用二维数组来构建测试所需要的参数列表，其中每个数组中的元素的放置顺序并没有什么要求，只要和构造函数中的顺序保持一致就可以了。现在如果再增加一种测试情况，只需要在静态方法 words 中添加相应的数组即可，不再需要复制粘贴出一个新的方法出来了。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10205">
						<span class="atitle">JUnit 和 Ant</span>
				</a>
		</p>
		<p>随着项目的进展，项目的规模在不断的膨胀，为了保证项目的质量，有计划的执行全面的单元测试是非常有必要的。但单靠JUnit提供的测试套件很难胜任这项工作，因为项目中单元测试类的个数在不停的增加，测试套件却无法动态的识别新加入的单元测试类，需要手动修改测试套件，这是一个很容易遗忘得步骤，稍有疏忽就会影响全面单元测试的覆盖率。</p>
		<p>当然解决的方法有多种多样，其中将 JUnit 与构建利器 Ant 结合使用可以很简单的解决这个问题。Ant —— 备受赞誉的 Java 构建工具。它凭借出色的易用性、平台无关性以及对项目自动测试和自动部署的支持，成为众多项目构建过程中不可或缺的独立工具，并已经成为事实上的标准。Ant 内置了对 JUnit 的支持，它提供了两个 Task：junit 和 junitreport，分别用于执行 JUnit 单元测试和生成测试结果报告。使用这两个 Task 编写构建脚本，可以很简单的完成每次全面单元测试的任务。 </p>
		<p>不过，在使用 Ant 运行 JUnit 之前，您需要稍作一些配置。打开 Eclipse 首选项界面，选择 Ant -&gt; Runtime 首选项（见<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#figure007"><font color="#5c81a7">图7</font></a>），将 JUnit 4.1 的 JAR 文件添加到 Classpath Tab 页中的 Global Entries 设置项里。记得检查一下 Ant Home Entries 设置项中的 Ant 版本是否在 1.7.0 之上，如果不是请替换为最新版本的 Ant JAR 文件。</p>
		<br />
		<a name="figure007">
				<b>图7 Ant Runtime 首选项</b>
		</a>
		<br />
		<img alt="图7 Ant Runtime 首选项" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure007.jpg" />
		<br />
		<p>剩下的工作就是要编写 Ant 构建脚本 build.xml。虽然这个过程稍嫌繁琐，但这是一件一劳永逸的事情。现在我们就把前面编写的测试用例都放置到 Ant 构建脚本中执行，为项目 coolJUnit 的构建脚本添加一下内容：</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td class="code-outline">
										<pre class="displaycode">&lt;?xml version="1.0"?&gt;
&lt;!-- ============================================= 
     auto unittest task    
     ai92                                                                
     ========================================== --&gt;
&lt;project name="auto unittest task" default="junit and report" basedir="."&gt;

		&lt;property name="output folder" value="bin"/&gt;

		&lt;property name="src folder" value="src"/&gt;
	
		&lt;property name="test folder" value="testsrc"/&gt;
	
		&lt;property name="report folder" value="report" /&gt;

		&lt;!-- - - - - - - - - - - - - - - - - - 
          target: test report folder init                      
         - - - - - - - - - - - - - - - - - --&gt;
		&lt;target name="test init"&gt;
			&lt;mkdir dir="${report folder}"/&gt;
		&lt;/target&gt;
	
		&lt;!-- - - - - - - - - - - - - - - - - - 
          target: compile                      
         - - - - - - - - - - - - - - - - - --&gt;
		&lt;target name="compile"&gt;
			&lt;javac srcdir="${src folder}" destdir="${output folder}" /&gt;
			&lt;echo&gt;compilation complete!&lt;/echo&gt;
		&lt;/target&gt;

		&lt;!-- - - - - - - - - - - - - - - - - - 
          target: compile test cases                      
         - - - - - - - - - - - - - - - - - --&gt;
		&lt;target name="test compile" depends="test init"&gt;
			&lt;javac srcdir="${test folder}" destdir="${output folder}" /&gt;
			&lt;echo&gt;test compilation complete!&lt;/echo&gt;
		&lt;/target&gt;
	
		&lt;target name="all compile" depends="compile, test compile"&gt;
		&lt;/target&gt;
	
		&lt;!-- ======================================== 
          target: auto test all test case and output report file                      
      	===================================== --&gt;
		&lt;target name="junit and report" depends="all compile"&gt;
			&lt;junit printsummary="on" fork="true" showoutput="true"&gt;
				&lt;classpath&gt;
					&lt;fileset dir="lib" includes="**/*.jar"/&gt;
					&lt;pathelement path="${output folder}"/&gt;
				&lt;/classpath&gt;
				&lt;formatter type="xml" /&gt;
				&lt;batchtest todir="${report folder}"&gt;
					&lt;fileset dir="${output folder}"&gt;
						&lt;include name="**/Test*.*" /&gt;
					&lt;/fileset&gt;
				&lt;/batchtest&gt;
			&lt;/junit&gt;
			&lt;junitreport todir="${report folder}"&gt;
				&lt;fileset dir="${report folder}"&gt;
					&lt;include name="TEST-*.xml" /&gt;
				&lt;/fileset&gt;
				&lt;report format="frames" todir="${report folder}" /&gt;
			&lt;/junitreport&gt;
		&lt;/target&gt;
&lt;/project&gt;
</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>Target junit report 是 Ant 构建脚本中的核心内容，其它 target 都是为它的执行提供前期服务。Task junit 会寻找输出目录下所有命名以“Test”开头的 class 文件，并执行它们。紧接着 Task junitreport 会将执行结果生成 HTML 格式的测试报告（<a href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#figure008"><font color="#5c81a7">图8</font></a>）放置在“report folder”下。</p>
		<p>为整个项目的单元测试类确定一种命名风格。不仅是出于区分类别的考虑，这为 Ant 批量执行单元测试也非常有帮助，比如前面例子中的测试类都已“Test”打头，而测试套件则以“Suite”结尾等等。</p>
		<br />
		<a name="figure008">
				<b>图8 junitreport 生成的测试报告</b>
		</a>
		<br />
		<img alt="图8 junitreport 生成的测试报告" src="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/figure008.jpg" />
		<br />
		<p>现在执行一次全面的单元测试变得非常简单了，只需要运行一下 Ant 构建脚本，就可以走完所有流程，并能得到一份详尽的测试报告。您可以在 <a href="http://ant.apache.org/manual/index.html"><font color="#5c81a7">Ant 在线手册</font></a> 中获得上面提及的每一个 Ant 内置 task 的使用细节。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10249">
						<span class="atitle">总结</span>
				</a>
		</p>
		<p>随着越来越多的开发人员开始认同并接受极限编程（XP）的思想，单元测试的作用在软件工程中变得越来越重要。本文旨在将最新的单元测试工具 JUnit 4 介绍给您，以及如何结合 IDE Eclipse 和构建工具 Ant 创建自动化单元测试方案。并且还期望您能够通过本文“感染”一些好的单元测试意识，因为 JUnit 本身仅仅是一份工具而已，它的真正优势来自于它的思想和技术。</p>
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#main">
																				<b>
																						<font color="#5c81a7">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<span class="atitle">
						<a name="download">下载</a>
				</span>
		</p>
		<table class="data-table-1" cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<th scope="col">描述</th>
								<th scope="col">名字</th>
								<th scope="col" align="right">大小</th>
								<th scope="col">下载方法</th>
						</tr>
						<tr>
								<th class="tb-row" scope="row">本文示例代码</th>
								<td nowrap="">coolJUnit.zip</td>
								<td nowrap="" align="right">24 KB</td>
								<td nowrap="">
										<a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-lo-junit4/coolJUnit.zip">
												<b>
														<font color="#5c81a7">HTTP</font>
												</b>
										</a>
								</td>
						</tr>
				</tbody>
		</table>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr valign="top">
								<td colspan="5">
										<font color="#5c81a7">
												<img height="12" alt="" src="http://www.ibm.com/i/c.gif" width="12" border="0" />
										</font>
								</td>
						</tr>
						<tr>
								<td>
										<font color="#5c81a7">
												<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/fw.gif" width="16" />
										</font>
								</td>
								<td>
										<a class="fbox" href="http://www.ibm.com/developerworks/cn/whichmethod.html">
												<font color="#5c81a7">关于下载方法的信息</font>
										</a>
								</td>
								<td>
										<font color="#5c81a7">
												<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="50" />
										</font>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="resources">
						<span class="atitle">参考资料 </span>
				</a>
		</p>
		<b>学习</b>
		<br />
		<ul>
				<li>
						<a href="http://www.ibm.com/developerworks/cn/java/j-junit4.html">
								<font color="#5c81a7">JUnit 4 抢先看</font>
						</a>（Elliotte Rusty Harold, developerWorks, 2005 年 10 月）：Elliotte Rusty Harold 为大家揭开了 JUnit 4 新框架的面纱。<br /><br /></li>
				<li>
						<a href="http://www.ibm.com/developerworks/cn/java/j-cq08296/">
								<font color="#5c81a7">追求代码质量: JUnit 4 与 TestNG 的对比</font>
						</a>（Andrew Glover, developerWorks, 2006 年 9 月）：Andrew Glover 探讨了这两种框架各自的独特之处，并阐述了 TestNG 独有的三种高级测试特性。<br /><br /></li>
				<li>
						<a href="http://www.ibm.com/developerworks/cn/java/j-testng/">
								<font color="#5c81a7">TestNG 使 Java 单元测试轻而易举</font>
						</a>（Filippo Diotalevi, developerWorks, 2005 年 1 月）：TestNG 不仅确实强大、创新、可扩展、灵活，它还展示了 Java Annotations（JDK 5.0 中的重大新特性）的有趣应用。<br /><br /></li>
				<li>
						<a href="http://www.ibm.com/developerworks/cn/java/j-annotate1/">
								<font color="#5c81a7">Tiger 中的注释，第 1 部分: 向 Java 代码中添加元数据</font>
						</a>（Brett McLaughlin, developerWorks, 2004 年 9 月）：本文解释了元数据如此有用的原因，向您介绍了 Java 语言中的注释，并研究了 Tiger 的内置注释。 <br /><br /></li>
				<li>
						<a href="http://www.ibm.com/developerworks/cn/java/j-ant/index.html">
								<font color="#5c81a7">利用 Ant 和 JUnit 进行增量开发</font>
						</a>（Malcolm Davis, developerWorks, 2000 年 11 月）：本文通过使用代码样本说明了单元测试的种种好处，特别是使用 Ant 和 JUnit 带来的各种方便。 <br /><br /></li>
				<li>
						<a href="http://www.ibm.com/developerworks/cn/java/j-cq/">
								<i>
										<font color="#5c81a7">追求代码质量系列</font>
								</i>
						</a>（Andrew Glover, developerWorks）：在这个系列中，Andrew Glover 将重点阐述有关保证代码质量的一些有时看上去有点神秘的东西。 <br /><br /></li>
		</ul>
		<br />
		<b>获得产品和技术</b>
		<br />
		<ul>
				<li>
						<a href="http://www.junit.org/">
								<font color="#5c81a7">获得 JUnit 软件和相关信息</font>
						</a>。<br /><br /></li>
				<li>
						<a href="http://ant.apache.org/">
								<font color="#5c81a7">获得 Ant 软件和相关信息</font>
						</a>。<br /><br /></li>
				<li>
						<a href="http://www.eclipse.org/">
								<font color="#996699">获得 Eclipse 软件和相关信息</font>
						</a>。</li>
		</ul>
		<br />
<img src ="http://www.blogjava.net/relax/aggbug/101945.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/relax/" target="_blank">轻松</a> 2007-03-05 16:15 <a href="http://www.blogjava.net/relax/archive/2007/03/05/101945.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[软件测试]HttpUnit-测试用例(例子)[ZZ]</title><link>http://www.blogjava.net/relax/archive/2005/01/31/867.html</link><dc:creator>轻松</dc:creator><author>轻松</author><pubDate>Mon, 31 Jan 2005 07:06:00 GMT</pubDate><guid>http://www.blogjava.net/relax/archive/2005/01/31/867.html</guid><wfw:comment>http://www.blogjava.net/relax/comments/867.html</wfw:comment><comments>http://www.blogjava.net/relax/archive/2005/01/31/867.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/relax/comments/commentRss/867.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/relax/services/trackbacks/867.html</trackback:ping><description><![CDATA[<P><STRONG>(1)环境设置：导入HttpUnit</STRONG></P>
<P><STRONG></STRONG></P>
<P><STRONG>(2)开始实践，写一个测试接口，起名为LoginTestInf：</STRONG></P>
<P>/*<BR>&nbsp;* Created on 2004-12-17<BR>&nbsp;*<BR>&nbsp;* TODO To change the template for this generated file go to<BR>&nbsp;* Window - Preferences - Java - Code Style - Code Templates<BR>&nbsp;*/<BR>package org.apollo.test.util;</P>
<P>/**<BR>&nbsp;* @author&nbsp;SixSun<BR>&nbsp;*<BR>&nbsp;* TODO To change the template for this generated type comment go to<BR>&nbsp;* Window - Preferences - Java - Code Style - Code Templates<BR>&nbsp;*/</P>
<P>/**<BR>&nbsp;*测试用例编号 : 0001<BR>&nbsp;*测试用例名称 : HttpUnit 登陆验证测试用例<BR>&nbsp;*测试目标 : 验证用户登陆是否成功<BR>&nbsp;*测试过程 :<BR>&nbsp;*1、输入登陆地址的页面地址，验证该页面是否可被正常访问。<BR>&nbsp;*2、验证被访问的页面是否是登陆页面。<BR>&nbsp;*3、输入非法用户名、密码，验证登陆失败。<BR>&nbsp;*4、输入合法用户名、密码，验证登陆成功。<BR>&nbsp;*/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>public interface LoginTestInf {<BR>&nbsp;&nbsp;&nbsp; public void testValidPage() throws Exception;<BR>&nbsp;&nbsp;&nbsp; public void testIsLoginPage() throws Exception;<BR>&nbsp;&nbsp;&nbsp; public void testBadLogin() throws Exception;<BR>&nbsp;&nbsp;&nbsp; public void testGoodLogin() throws Exception;<BR>}<BR></P>
<P><STRONG>(3)实现一个Junit TestCase 同时 implements LoginTestInf 接口：</STRONG></P>
<P>/*<BR>&nbsp;* Created on 2004-12-17<BR>&nbsp;*<BR>&nbsp;* TODO To change the template for this generated file go to<BR>&nbsp;* Window - Preferences - Java - Code Style - Code Templates<BR>&nbsp;*/<BR>package org.apollo.test.util;</P>
<P>import java.net.URL;<BR>import junit.framework.TestCase;<BR>import junit.framework.TestSuite;<BR>import junit.textui.TestRunner;<BR>&nbsp;<BR>import com.meterware.httpunit.WebConversation;<BR>import com.meterware.httpunit.WebRequest;<BR>import com.meterware.httpunit.WebResponse;<BR>import com.meterware.httpunit.WebForm;<BR>import com.meterware.httpunit.GetMethodWebRequest;</P>
<P>import org.apollo.test.util.LoginTestInf;</P>
<P>/**<BR>&nbsp;* @author&nbsp;sixsun<BR>&nbsp;*<BR>&nbsp;* TODO To change the template for this generated type comment go to<BR>&nbsp;* Window - Preferences - Java - Code Style - Code Templates<BR>&nbsp;*/<BR>public class LoginTest extends TestCase implements LoginTestInf {</P>
<P>&nbsp;private String username = "suibian";<BR>&nbsp;private String password = "suibian";<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp; private WebConversation browser;<BR>&nbsp;&nbsp;&nbsp; private WebRequest requestIndex;<BR>&nbsp;&nbsp;&nbsp; private WebRequest requestLogin;<BR>&nbsp;&nbsp;&nbsp; private WebResponse responseIndex;<BR>&nbsp;&nbsp;&nbsp; private WebResponse responseLogin;<BR>&nbsp;&nbsp;&nbsp; private String urlSystem = "系统首页网址";<BR>&nbsp;&nbsp;&nbsp; private String urlLogin = "登陆界面网址";<BR>&nbsp;/*<BR>&nbsp; * @see TestCase#setUp()<BR>&nbsp; */<BR>&nbsp;protected void setUp() throws Exception {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; browser =&nbsp; new WebConversation();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; requestIndex = new GetMethodWebRequest(urlSystem);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; responseIndex&nbsp; = browser.getResponse(requestIndex);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; requestLogin = new GetMethodWebRequest(urlLogin);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; responseLogin&nbsp; = browser.getResponse(requestLogin);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;}<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp; //输入登陆地址的页面地址，验证该页面是否可被正常访问<BR>&nbsp;&nbsp;&nbsp; public void testValidPage() throws Exception{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertNotNull("zsonline在网络上不存在！",responseIndex);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; //验证被访问的页面是否是登陆页面<BR>&nbsp;&nbsp;&nbsp; public void testIsLoginPage() throws Exception{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; URL currentUrl = responseLogin.getURL();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String currentUrlStr = currentUrl.getProtocol() + "://" +currentUrl.getHost() + currentUrl.getPath();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertEquals("登陆页面不是zsonline首页!" ,currentUrlStr,urlLogin);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; //输入非法用户名、密码，验证登陆失败<BR>&nbsp;&nbsp;&nbsp; public void testBadLogin() throws Exception{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WebForm form = responseLogin.getForms()[0];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; form.setParameter("username","badname");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; form.setParameter("password","badpassword");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; requestLogin = form.getRequest();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; responseLogin =&nbsp; browser.getResponse(requestLogin);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertTrue("用户名不存在，请确认用户名输入是否完全正确(区分大小写)！",<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; responseLogin.getText().indexOf("用户名不存在，请确认用户名输入是否完全正确(区分大小写)！") != -1);<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp; //输入合法用户名、密码，验证登陆成功<BR>&nbsp;&nbsp;&nbsp; public void testGoodLogin() throws Exception{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WebForm form = responseLogin.getForms()[0];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; form.setParameter("username",username);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; form.setParameter("password",password);//此处需要填写真实密码<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; requestLogin = form.getRequest();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; responseLogin =&nbsp; browser.getResponse(requestLogin);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertTrue("转到'zsonline'【suibian】用户首页失败！",responseLogin.getText().indexOf("用户测试用户_zsonline，您好！") != -1);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp; public static TestSuite suite(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new TestSuite(LoginTest.class);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String args[]){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TestRunner.run(suite());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>}</P><img src ="http://www.blogjava.net/relax/aggbug/867.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/relax/" target="_blank">轻松</a> 2005-01-31 15:06 <a href="http://www.blogjava.net/relax/archive/2005/01/31/867.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Eclipse中使用JUnit(翻译)</title><link>http://www.blogjava.net/relax/archive/2005/01/28/782.html</link><dc:creator>轻松</dc:creator><author>轻松</author><pubDate>Fri, 28 Jan 2005 05:34:00 GMT</pubDate><guid>http://www.blogjava.net/relax/archive/2005/01/28/782.html</guid><wfw:comment>http://www.blogjava.net/relax/comments/782.html</wfw:comment><comments>http://www.blogjava.net/relax/archive/2005/01/28/782.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/relax/comments/commentRss/782.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/relax/services/trackbacks/782.html</trackback:ping><description><![CDATA[<TABLE width="92%" border=0>
<TBODY>
<TR>
<TD align=left colSpan=3>这篇文章将向你介绍Junit，一个用来在项目中进行测试和调试的工具。在介绍完TDD（以测试驱动开发）理论后，将进一步讲解怎样在流行的Eclipse中建立你自己的JUnit测试。向你展示如何测试Hello&nbsp;World这样简单的程序。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;许多书上都讨论了自动测试，但是只有很少的著作注意到这么一个问题，那就是怎样把这些测试组织起来。随着测试的增加，放置和调用这些测试却变得更加麻烦。这将成为一个重要问题，以至于出现了TDD，极限编程（XP）使TDD得以普及。另外，你可以这样理解TDD：通过测试来开发。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;TDD的主要规范&nbsp;:<BR>&nbsp;&nbsp;&nbsp;&nbsp;在编写程序代码之前，与之对应的自动测试必须被写好。甚至程序代码并不存在，那也要看见一个失败的测试结果。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;在测试通过后，副本代码必须被丢弃。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;有一个具体步骤（可能指的是《Extreme&nbsp;Programming》）可以被任何一个程序员来参考，而不需要特殊的其他方法。在我们开始写测试之前，这些步骤（章节）应该被首先阅读——怎样组织自动测试。<BR>&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;讲解一下不同种类的测试：<BR>&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;单元测试：检测模块（也就是类）的正确性。如果对象需要访问外部的数据资源，例如数据库，就需要模拟一个mock&nbsp;objects，但在实际中真实数据与测试环境是不同的。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;客户测试：这是功能性、系统、和验收测试。用来测试整体的系统特性。在XP中，这些测试由用户编写。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;综合测试：介于用户测试和单元测试之间的桥梁。综合测试帮助测试应用程序的交互性。一般情况下，mock&nbsp;objects不被用于综合测试，它会增加测试时间。同样，综合测试经常依赖特殊的测试环境，例如数据库送来的测试数据。综合测试也需要用到外部类库。例如为J2EE应用程序进行综合测试的类库Cactus。解释这些测试超出了本文的范围，需要更加详细的信息请参考http://jakarta.apache.org/cactus/。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;开发人员测试：这是用来让开发人员检验自己代码或新函数的。对于每一个开发人员，只要有可能，就需要有更多的测试来检验代码。组织这些测试和组织程序代码一样重要。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;在以下章节，只要提到“测试”，那就指的是开发人员测试。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;我们几乎准备好开始建立测试了，先应该为我们的测试选择名字。你也许会说，“这不是问题：把‘Test’这个字放在类名前面，就好了！”不会这么快！让我来说一下这个步骤存在的问题：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;在TDD中，被测试的类或者方法还不存在。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;一个测试能够覆盖多个方法，甚至多个类，这是可能的。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;以上只是一些普遍问题；还存在更多的问题。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;让我来提一个建议，在测试命名时：测试类的名字应该让人一眼就知道这是一个测试类，且能说明它要测试什么，注意是否和其他类重名。按照以上建议做，就很简单了，也不用担心名字太长或难听。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;即将在Eclipse中用JUnit工具创建我们第一个测试了。假设你已经下载了一个最新的Eclipse版本。如果还没有，你应该去官方站点http://www.eclipse.org下载。还需要JUnit，也可以从http://www.junit.org/下载。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;运行Eclipse。新建一个workplace项目，点击文件-&gt;新建-&gt;项目，选择Java项目，点击下一步。起一个项目名称，例如ProjectWithJUnit。点击完成。这样就完成新项目的建立了。再来配置一下Eclipse，在构建路径中添加JUnit类库。在工具条上点击项目-&gt;属性，选择Java构建路径，库，选择添加外部JAR，浏览Junit被存储的目录，选择junit.jar，点击打开。你将会看见JUnit出现在库的列表中。点击确定，让Eclipse重建路径。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;现在开发我们的“Hello&nbsp;World”例子。按照TDD的规则，应该在代码建立以前先把测试写好。为了能够在某出开始，我们假设未来的类名是HelloWorld，并且有一个方法Say()，这个方法返回String的值（例如“Hello&nbsp;World!”）。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;建立测试，在ProjectWithJUnit的标题上面点击右键，选择新建-&gt;其他，展开“Java”选项，选择JUnit。在右边的栏目对话框中选择测试案例，然后下一步。参考图1。<BR><A href="http://www.onjava.com/onjava/2004/02/04/graphics/juie1ss.gif" target=_blank><IMG title="open in new window" alt="" src="http://www.onjava.com/onjava/2004/02/04/graphics/juie1ss.gif" border=0></A><BR>图1.&nbsp;在Eclipse中建立JUnit测试<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;在测试类这一栏中，写上将要被测试的类名HelloWorld。选择一个测试案例的名字，例如TestThatWeGetHelloWorldPrompt（是的，看上去很长，但是很清楚它的行为。）点击完成。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;TestThatWeGetHelloWorldPrompt的代码如下：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;import&nbsp;junit.framework.TestCase;<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;class&nbsp;TestThatWeGetHelloWorldPrompt<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;extends&nbsp;TestCase&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;TestThatWeGetHelloWorldPrompt(<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;name)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super(name);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;testSay()&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HelloWorld&nbsp;hi&nbsp;=&nbsp;new&nbsp;HelloWorld();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(\"Hello&nbsp;World!\",&nbsp;hi.say());<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;junit.textui.TestRunner.run(<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestThatWeGetHelloWorldPrompt.class);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;代码并不复杂；只是有点与众不同。然而，让我们考察一下细节。我们继承了JUnit的TestCase类，它在JUnit的javadocs定义为“运行众多测试的夹具。”JUnit也有TestSuite类，它是一组测试案例的集合，但在本文中不做讨论。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;建立测试案例的步骤如下：<BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;1、建立一个junit.framework.TestCase的实例。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;2、定义一些以“test”开头的无返回方法（例如testWasTransactionSuccessful()，testShow()，等等）。<BR><BR>&nbsp;&nbsp;&nbsp;TestThatWeGetHelloWorldPrompt.java包含这些：TestCase的子类和一个叫做testSay()的方法。这个方法调用了assertEquals()函数，它用来比较我们预期的值和由say()返回的值。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;main()方法用来运行测试和显示输出的。JUnit的TestRunner处理测试，提供基于图像和文本的输出表现形式。我们使用基于文本的版本，因为Eclipse支持它，且也适合我们。当开始运行后，基于文本的版本测试会以文本形式输出，Eclipse会把这些输出自动变成图像界面的输出。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;按照TDD规范，首次运行测试，应该故意让它失败。点击运行-&gt;运行为-&gt;Junit测试（记住TestThatWeGetHelloWorldPrompt.java应该被突出的显示在包资源管理器中）。在左边窗口，应该看见JUnit窗口而不是包资源管理器，它显示一个红条，一次失败的测试，具体的失败原因参看图2。如果没有自动显示这些内容，点击JUnit标签（在底部的左边）。<BR><BR><A href="http://www.onjava.com/onjava/2004/02/04/graphics/juie2ss.gif" target=_blank><IMG title="open in new window" alt="" src="http://www.onjava.com/onjava/2004/02/04/graphics/juie2ss.gif" border=0></A><BR>图2.&nbsp;JUnit中失败的测试<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;很好！的却失败了。现在我们来建立被测试代码：在包资源管理器窗口的ProjectWithJUnit标题上右击，选择新建-&gt;类。选择类名，我们已经假设了它叫HelloWorld，然后直接点击完成。为HelloWorld.java填入下列代码：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;class&nbsp;HelloWorld&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;String&nbsp;say()&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return(\"Hello&nbsp;World!\");<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;这段代码很简单，甚至不需要注解，我们再来看看结果。按照上面描述过的方式，在JUnit的窗口中显示了一个绿条，参看图3。绿条证明测试成功。<BR><BR><A href="http://www.onjava.com/onjava/2004/02/04/graphics/juie3ss.gif" target=_blank><IMG title="open in new window" alt="" src="http://www.onjava.com/onjava/2004/02/04/graphics/juie3ss.gif" border=0></A><BR>图3.&nbsp;JUnit中成功的测试<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;现在，我们想再让测试失败一次，但原因不同。这有助于展示JUnit测试中不同的报错信息。修改assertEquals()代码，把“Hello&nbsp;World!”变成“Hello&nbsp;Me!”。当再次运行JUnit时，结果变成了红条，在JUnit窗口的底部输出了失败原因，参看图4。<BR><A href="http://www.onjava.com/onjava/2004/02/04/graphics/juie4ss.gif" target=_blank><IMG title="open in new window" alt="" src="http://www.onjava.com/onjava/2004/02/04/graphics/juie4ss.gif" border=0></A><BR>图4.&nbsp;JUnit中的ComparisonError<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;最后，我想说一下关于测试是开发过程中的必要部分的话题。测试代码一直是开发中的重要部分。经过近几年的发展，已得到了很大的提高，这要归功于强大的理论研究（比如“expectations-based&nbsp;development”等等），和快速发展的测试工具包，还有测试过程的改进。如果你对这篇文章感兴趣，那请你花一些时间来正式的学习一下测试理论吧，这对你的工作很有用。<BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;关于作者：<BR>&nbsp;&nbsp;&nbsp;&nbsp;Alexander&nbsp;Prohorenko&nbsp;一名UNIX系统管理员、网络安全管理员。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;Olexiy&nbsp;Prohorenko&nbsp;&nbsp;&nbsp;&nbsp;一名Java开发者居住在乌克兰的Dniepropetrovsk<BR></TD></TR>
<TR>
<TD colSpan=3 height=15>&nbsp;</TD></TR>
<TR>
<TD align=right colSpan=3>整理发布：独孤求胜</TD></TR>
<TR>
<TD align=right colSpan=3>摘自：CSDN.net</TD></TR>
<TR>
<TD align=right colSpan=3></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/relax/aggbug/782.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/relax/" target="_blank">轻松</a> 2005-01-28 13:34 <a href="http://www.blogjava.net/relax/archive/2005/01/28/782.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> junit使用简明手册</title><link>http://www.blogjava.net/relax/archive/2005/01/28/780.html</link><dc:creator>轻松</dc:creator><author>轻松</author><pubDate>Fri, 28 Jan 2005 05:13:00 GMT</pubDate><guid>http://www.blogjava.net/relax/archive/2005/01/28/780.html</guid><wfw:comment>http://www.blogjava.net/relax/comments/780.html</wfw:comment><comments>http://www.blogjava.net/relax/archive/2005/01/28/780.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/relax/comments/commentRss/780.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/relax/services/trackbacks/780.html</trackback:ping><description><![CDATA[用XP进行开发的过程，unit test是必不可少的环节。作为unit test，junit是首选的工具。本文从使用目的、如何使用、以及使用中需要考虑的问题，简略描述了junit的基本用法。<BR><BR>使用目的<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;junit是java中书写unit test的framework，目前一些流行的unit test工具大都都是在junit上扩展而来的。目前它的版本是junit3.8.1，可以从www.junit.org上下载。<BR><BR>用法<BR>1. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;基本使用步骤，Junit的使用非常简单，它的基本使用步骤：<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;创建，从junit.framework.TestCase派生unit test需要的test case<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;书写测试方法，提供类似于如下函数签名的测试方法：<BR><BR>public void testXXXXX();<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编译，书写完test case后，编译所写的test case类<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;运行，启动junit test runner，来运行这个test case。<BR><BR>Junit提供了2个基本的test runner：字符界面和图形界面。启动命令分别如下：<BR><BR>a 图形界面：<BR><BR>java junit.swingui.TestRunner XXXXX<BR><BR>b 字符界面：<BR><BR>java junit.textui.TestRunner XXXXX<BR><BR>2. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用例子：<BR><BR>import junit.frmework.TestCase;<BR><BR>public class TestSample extends TestCaset{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void testMethod1(){<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertTrue( true);<BR><BR>}<BR><BR>}<BR><BR>3. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setUp与tearDown，这两个函数是junit framework中提供初始化和反初始化每个测试方法的。setUp在每个测试方法调用前被调用，负责初始化测试方法所需要的测试环境；tearDown在每个测试方法被调用之后被调用，负责撤销测试环境。它们与测试方法的关系可以描述如下：<BR><BR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;测试开始 -&gt; setUp -&gt; testXXXX -&gt; tearDown -&gt;测试结束<BR><BR><BR><BR><BR>4. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用例子：<BR><BR>import junit.frmework.TestCase;<BR><BR>public class TestSample extends TestCaset{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;protected void setUp(){<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//初始化……<BR><BR>}<BR><BR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void testMethod1(){<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertTrue( true);<BR><BR>}<BR><BR><BR><BR>potected void tearDown(){<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//撤销初始化……<BR><BR>}<BR><BR>}<BR><BR>5. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;区分fail、exception。<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fail，期望出现的错误。产生原因：assert函数出错（如assertFalse(true)）；fail函数产生（如fail(……)）。<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exception，不期望出现的错误，属于unit test程序运行时抛出的异常。它和普通代码运行过程中抛出的runtime异常属于一种类型。<BR><BR>对于assert、fail等函数请参见junit的javadoc。<BR><BR>6. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用例子：<BR><BR>import junit.frmework.TestCase;<BR><BR>public class TestSample extends TestCaset{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;protected void setUp(){<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//初始化……<BR><BR>}<BR><BR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void testMethod1(){<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;……<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;boolean b= ……<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertTrue( b);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new Exception( “This is a test.”);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fail( “Unable point.”); &nbsp;&nbsp;&nbsp;&nbsp;//不可能到达<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}catch(Exception e){<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fail( “Yes, I catch u”); //应该到达点<BR><BR>}<BR><BR>……<BR><BR>}<BR><BR><BR><BR>potected void tearDown(){<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//撤销初始化……<BR><BR>}<BR><BR>}<BR><BR>7. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;组装TestSuite，运行更多的test。在junit中，Test、TestCase和TestSuite三者组成了composiste pattern。通过组装自己的TestSuite，可以完成对添加到这个TestSuite中的所有的TestCase的调用。而且这些定义的TestSuite还可以组装成更大的TestSuite，这样同时也方便了对于不断增加的TestCase的管理和维护。<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;它的另一个好处就是，可以从这个TestCase树的任意一个节点（TestSuite或TestCase）开始调用，来完成这个节点以下的所有TestCase的调用。提高了unit test的灵活性。<BR><BR>8. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用例子：<BR><BR>import junit.framework.Test;<BR><BR>import junit.framework.TestSuite;<BR><BR>public class TestAll{<BR><BR>public class TestAll{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//定义一个suite，对于junit的作用可以视为类似于java应用程序的main。<BR><BR>&nbsp;&nbsp;&nbsp;public static Test suite(){<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestSuite suite = new TestSuite("Running all tests.");<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;suite.addTestSuite( TestCase1.class);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;suite.addTestSuite( TestCase2.class);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return suite;<BR><BR>&nbsp;&nbsp;&nbsp;}<BR><BR>}<BR><BR>运行同运行单独的一个TestCase是一样的，参见step 1 “运行”。<BR><BR>9. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用Ant junit task。我们除了使用java来直接运行junit之外，我们还可以使用junit提供的junit task与ant结合来运行。涉及的几个主要的ant task如下：<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;junit&gt;，定义一个junit task<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;batchtest&gt;，位于&lt;junit&gt;中，运行多个TestCase<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;test&gt;，位于&lt;junit&gt;中，运行单个TestCase<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;formatter&gt;，位于&lt;junit&gt;中，定义一个测试结果输出格式<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;junitreport&gt;，定义一个junitreport task<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;report&gt;，位于&lt;junitreport&gt;中，输出一个junit report<BR><BR>具体的语法请参见相关文档。<BR><BR>10. &nbsp;&nbsp;使用例子：<BR><BR>&lt;junit printsummary="yes" haltonfailure="no"&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&lt;classpath&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;path refid="classpath"/&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="${dist.junit}"/&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&lt;/classpath&gt;<BR><BR>&nbsp;&nbsp;&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&lt;formatter type="brief" usefile="false"/&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&lt;formatter type="xml"/&gt;<BR><BR><BR><BR>&nbsp;&nbsp;&nbsp;&lt;batchtest todir="${doc.junitReport}"&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;fileset dir="${dist.junit}" includes="**/*Test.class" /&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&lt;/batchtest&gt;<BR><BR>&lt;/junit&gt;<BR><BR><BR><BR>&lt;junitreport todir="${doc.junitReport}"&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&lt;fileset dir="${doc.junitReport}"&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;include name="TEST*-*.xml"/&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&lt;/fileset&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&lt;report format="frames" styledir="${junit.styleDir}" todir="${doc.junitReport}"/&gt;<BR><BR>&lt;/junitreport&gt;<BR><BR>检查表<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;junit的使用并不很难，然而要书写一个好的TestCase却并非易事。一个不好的TestCase往往是既浪费了时间，也起不了实际的作用。相反，一个好的TestCase，不仅可以很好的指出代码中存在的问题，而且也可以作为代码更准确的文档，同时还在持续集成的过程中起非常重要的作用。在此给出书写TestCase时需要注意的几点：<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;测试的独立性：一次只测试一个对象，方便定位出错的位置。这有2层意思：一个TestCase，只测试一个对象；一个TestMethod，只测试这个对象中的一个方法。<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;给测试方法一个合适的名字。<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在assert函数中给出失败的原因，如：assertTrue( “… should be true”, &nbsp;……)，方便查错。在这个例子中，如果无法通过assertTrue，那么给出的消息将被显示。在junit中每个assert函数都有第一个参数是出错时显示消息的函数原型。<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;测试所有可能引起失败的地方，如：一个类中频繁改动的函数。对于那些仅仅只含有getter/setter的类，如果是由IDE（如Eclipse）产生的，则可不测；如果是人工写，那么最好测试一下。<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在setUp和tearDown中的代码不应该是与测试方法相关的，而应该是全局相关的。如针对与测试方法A和B，在setUp和tearDown中的代码应该是A和B都需要的代码。<BR><BR>- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;测试代码的组织：相同的包，不同的目录。这样，测试代码可以访问被测试类的protected变量/方法，方便测试代码的编写。放在不同的目录，则方便了测试代码的管理以及代码的打包和发布。一个例子如下：<BR><BR>src &nbsp;&nbsp;&lt;=源代码根目录<BR><BR><IMG style="VERTICAL-ALIGN: middle" height=19 src="http://www.uml.org.cn/j2ee/images/016.gif" width=19 border=0>-com<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<IMG style="VERTICAL-ALIGN: middle" height=19 src="http://www.uml.org.cn/j2ee/images/016.gif" width=19 border=0>-mod1<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<IMG style="VERTICAL-ALIGN: middle" height=19 src="http://www.uml.org.cn/j2ee/images/016.gif" width=19 border=0>-class1<BR><BR>junit &nbsp;&nbsp;&lt;=测试代码根目录<BR><BR><IMG style="VERTICAL-ALIGN: middle" height=19 src="http://www.uml.org.cn/j2ee/images/016.gif" width=19 border=0>-com<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<IMG style="VERTICAL-ALIGN: middle" height=19 src="http://www.uml.org.cn/j2ee/images/016.gif" width=19 border=0>-mod1<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<IMG style="VERTICAL-ALIGN: middle" height=19 src="http://www.uml.org.cn/j2ee/images/016.gif" width=19 border=0>-class1 <BR><img src ="http://www.blogjava.net/relax/aggbug/780.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/relax/" target="_blank">轻松</a> 2005-01-28 13:13 <a href="http://www.blogjava.net/relax/archive/2005/01/28/780.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用HttpUnit进行集成测试 </title><link>http://www.blogjava.net/relax/archive/2005/01/27/743.html</link><dc:creator>轻松</dc:creator><author>轻松</author><pubDate>Thu, 27 Jan 2005 01:56:00 GMT</pubDate><guid>http://www.blogjava.net/relax/archive/2005/01/27/743.html</guid><wfw:comment>http://www.blogjava.net/relax/comments/743.html</wfw:comment><comments>http://www.blogjava.net/relax/archive/2005/01/27/743.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/relax/comments/commentRss/743.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/relax/services/trackbacks/743.html</trackback:ping><description><![CDATA[<P><B>内容摘要</B></P>
<P class=normal>HttpUnit是一个集成测试工具，主要关注Web应用的测试，提供的帮助类让测试者可以通过Java类和服务器进行交互，并且将服务器端的响应当作文本或者DOM对象进行处理。HttpUnit还提供了一个模拟Servlet容器，让你可以不需要发布Servlet，就可以对Servlet的内部代码进行测试。本文中作者将详细的介绍如何使用HttpUnit提供的类完成集成测试。</P>
<P><B>1&nbsp;&nbsp;HttpUnit简介</B></P>
<P class=normal>HttpUnit是SourceForge下面的一个开源项目，它是基于JUnit的一个测试框架，主要关注于测试Web应用，解决使用JUnit框架无法对远程Web内容进行测试的弊端。当前的最新版本是1.5.4。为了让HtpUnit正常运行，你应该安装JDK1.3.1或者以上版本。</P>
<P class=normal><B>1.1&nbsp;&nbsp;工作原理</B></P>
<P class=normal>HttpUnit通过模拟浏览器的行为，处理页面框架（frames）,cookies,页面跳转（redirects）等。通过HttpUnit提供的功能，你可以和服务器端进行信息交互，将返回的网页内容作为普通文本、XML Dom对象或者是作为链接、页面框架、图像、表单、表格等的集合进行处理，然后使用JUnit框架进行测试，还可以导向一个新的页面，然后进行新页面的处理，这个功能使你可以处理一组在一个操作链中的页面。</P>
<P class=normal><B>1.2&nbsp;&nbsp;和其他商业工具的对比</B></P>
<P class=normal>商业工具一般使用记录、回放的功能来实现测试，但是这里有个缺陷，就是当页面设计被修改以后，这些被记录的行为就不能重用了，需要重新录制才能继续测试。</P>
<P class=normal>举个例子：如果页面上有个元素最先的设计是采用单选框，这个时候你开始测试，那么这些工具记录的就是你的单项选择动作，但是如果你的设计发生了变化，比如说我改成了下拉选择，或者使用文本框接受用户输入，这时候，你以前录制的测试过程就无效了，必须要重新录制。</P>
<P class=normal>而HttpUnit因为关注点是这些控件的内容，所以不管你的外在表现形式如何变化，都不影响你已确定测试的可重用性。</P>
<P class=normal>更多的关于httpunit的信息请访问httpunit的主页<A href="http://httpunit.sourceforge.net/"><FONT color=#9966cc>http://httpunit.sourceforge.net</FONT></A></P>
<P><B>2&nbsp;&nbsp;作者的演示环境</B></P>
<P class=normal>系统平台：Windows 2000 Server</P>
<P class=normal>应用服务器：深圳金蝶的apusic3.0</P>
<P class=normal>开发工具： eclipse 2.1.2</P>
<P><B>3&nbsp;&nbsp;HttpUnit安装、环境配置</B></P>
<P class=normal><B>3.1&nbsp;&nbsp;安装</B></P>
<P class=normal>1. 到HttpUnit的主页http://httpunit.sourceforge.net下载最新的包文件，当前的最新版本是1.5.4。 
<P class=normal>2. 将下载的Zip包解压缩到c:/httpunit(后面将使用%httpunit_home%引用该目录) 
<P class=normal><B>3.2&nbsp;&nbsp;环境配置</B></P>
<P class=normal>作者的演示程序都是在eclipse中开发、执行的，所以环境配置都是以eclipse为例，如果你使用其他的开发工具，请根据这些步骤进行环境配置。</P>
<OL>
<LI>启动eclipse，建立一个java工程 
<LI>将%httpunit_home%/lib/*.jar; %httpunit_home%/jars/*.jar加入到该java工程的Java build Path变量中 </LI></OL>
<P><B>4&nbsp;&nbsp;如何使用httpunit处理页面的内容</B></P>
<P class=normal>WebConversation类是HttpUnit框架中最重要的类，它用于模拟浏览器的行为。其他几个重要的类是：</P>
<P class=normal>WebRequest类，模仿客户请求，通过它可以向服务器发送信息。</P>
<P class=normal>WebResponse类，模拟浏览器获取服务器端的响应信息。</P>
<P class=normal><B>4.1&nbsp;&nbsp;获取指定页面的内容</B></P>
<P class=normal><B>4.1.1&nbsp;&nbsp;直接获取页面内容</B></P><PRE>System.out.println("直接获取网页内容：");
//建立一个WebConversation实例
WebConversation wc = new WebConversation();

//向指定的URL发出请求，获取响应
WebResponse wr = wc.getResponse( "http://localhost:6888/HelloWorld.html" );

//用getText方法获取相应的全部内容
//用System.out.println将获取的内容打印在控制台上
System.out.println( wr.getText() );
</PRE>
<P class=normal><B>4.1.2&nbsp;&nbsp;通过Get方法访问页面并且加入参数</B></P><PRE>System.out.println("向服务器发送数据，然后获取网页内容：");
//建立一个WebConversation实例
WebConversation wc = new WebConversation();
//向指定的URL发出请求
WebRequest req = new GetMethodWebRequest( "http://localhost:6888/HelloWorld.jsp" );
//给请求加上参数  
req.setParameter("username","姓名");
//获取响应对象
WebResponse resp = wc.getResponse( req );

//用getText方法获取相应的全部内容
//用System.out.println将获取的内容打印在控制台上
System.out.println( resp.getText() );
</PRE>
<P class=normal><B>4.1.3&nbsp;通过Post方法访问页面并且加入参数</B></P><PRE>System.out.println("使用Post方式向服务器发送数据，然后获取网页内容：");
//建立一个WebConversation实例
WebConversation wc = new WebConversation();
//向指定的URL发出请求
WebRequest req = new PostMethodWebRequest( "http://localhost:6888/HelloWorld.jsp" );
//给请求加上参数  
req.setParameter("username","姓名");
//获取响应对象
WebResponse resp = wc.getResponse( req );

//用getText方法获取相应的全部内容
//用System.out.println将获取的内容打印在控制台上
System.out.println( resp.getText() );
</PRE>
<P class=normal>大家关注一下上面代码中打了下划线的两处内容，应该可以看到，使用Get、Post方法访问页面的区别就是使用的请求对象不同。</P>
<P class=normal><B>4.2&nbsp;&nbsp;处理页面中的链接</B></P>
<P class=normal>这里的演示是找到页面中的某一个链接，然后模拟用户的单机行为，获得它指向文件的内容。比如在我的页面HelloWorld.html中有一个链接，它显示的内容是TestLink，它指向我另一个页面TestLink.htm. TestLink.htm里面只显示TestLink.html几个字符。</P>
<P class=normal>下面是处理代码：</P><PRE>System.out.println("获取页面中链接指向页面的内容：");
//建立一个WebConversation实例
WebConversation wc = new WebConversation();
//获取响应对象
WebResponse   resp = wc.getResponse( "http://localhost:6888/HelloWorld.html" );
//获得页面链接对象
WebLink       link = resp.getLinkWith( "TestLink" );
//模拟用户单击事件 
link.click();
//获得当前的响应对象
WebResponse   nextLink = wc.getCurrentPage();                                           
   
//用getText方法获取相应的全部内容
//用System.out.println将获取的内容打印在控制台上
System.out.println( nextLink.getText() );
</PRE>
<P class=normal><B>4.3&nbsp;&nbsp;处理页面中的表格</B></P>
<P class=normal>表格是用来控制页面显示的常规对象，在HttpUnit中使用数组来处理页面中的多个表格，你可以用resp.getTables()方法获取页面所有的表格对象。他们依照出现在页面中的顺序保存在一个数组里面。</P>
<P class=normal>[注意] Java中数组下标是从0开始的，所以取第一个表格应该是resp.getTables()[0]，其他以此类推。</P>
<P class=normal>下面的例子演示如何从页面中取出第一个表格的内容并且将他们循环显示出来：</P><PRE>System.out.println("获取页面中表格的内容：");
//建立一个WebConversation实例
WebConversation wc = new WebConversation();
//获取响应对象
WebResponse   resp = wc.getResponse( "http://localhost:6888/HelloWorld.html" );
//获得对应的表格对象
WebTable webTable = resp.getTables()[0];
//将表格对象的内容传递给字符串数组
String[][] datas = webTable.asText();
//循环显示表格内容
int i = 0 ,j = 0;
int m = datas[0].length;
int n = datas.length;
while (i&lt;n){
  j=0;
  while(j&lt;m){
    System.out.println("表格中第"+(i+1)+"行第"+
 (j+1)+"列的内容是："+datas[i][j]);
    ++j;
  }
  ++i;
}
</PRE>
<P class=normal><B>4.4&nbsp;&nbsp;处理页面中的表单</B></P>
<P class=normal>表单是用来接受用户输入，也可以向用户显示用户已输入信息（如需要用户修改数据时，通常会显示他以前输入过的信息），在HttpUnit中使用数组来处理页面中的多个表单，你可以用resp.getForms()方法获取页面所有的表单对象。他们依照出现在页面中的顺序保存在一个数组里面。</P>
<P class=normal>[注意] Java中数组下标是从0开始的，所以取第一个表单应该是resp.getForms()[0]，其他以此类推。</P>
<P class=normal>下面的例子演示如何从页面中取出第一个表单的内容并且将他们循环显示出来：</P><PRE>System.out.println("获取页面中表单的内容：");
//建立一个WebConversation实例
WebConversation wc = new WebConversation();
//获取响应对象
WebResponse   resp = wc.getResponse( "http://localhost:6888/HelloWorld.html" );
//获得对应的表单对象
WebForm webForm = resp.getForms()[0];
//获得表单中所有控件的名字
String[] pNames = webForm.getParameterNames();
int i = 0;
int m = pNames.length;
//循环显示表单中所有控件的内容
while(i&lt;m){
   System.out.println("第"+(i+1)+"个控件的名字是"+pNames[i]+
   "，里面的内容是"+webForm.getParameterValue(pNames[i]));
   ++i;
}
</PRE>
<P><B>5&nbsp;&nbsp;如何使用httpunit进行测试</B></P>
<P class=normal><B>5.1&nbsp;&nbsp;对页面内容进行测试</B></P>
<P class=normal>httpunit中的这部分测试完全采用了JUnit的测试方法，即直接将你期望的结果和页面中的输出内容进行比较。不过这里的测试就简单多了，只是字符串和字符串的比较。</P>
<P class=normal>比如你期望中的页面显示是中有一个表格，它是页面中的第一个表格，而且他的第一行第一列的数据应该是显示username，那么你可以使用下面的代码进行自动化测试：</P><PRE>System.out.println("获取页面中表格的内容并且进行测试：");
//建立一个WebConversation实例
WebConversation wc = new WebConversation();
//获取响应对象
WebResponse   resp = wc.getResponse( "http://localhost:6888/TableTest.html" );
//获得对应的表格对象
WebTable webTable = resp.getTables()[0];
//将表格对象的内容传递给字符串数组
String[][] datas = webTable.asText();
//对表格内容进行测试
String expect = "中文";
Assert.assertEquals(expect,datas[0][0]);
</PRE>
<P class=normal><B>5.2&nbsp;&nbsp;对Servlet进行测试</B></P>
<P class=normal>除了对页面内容进行测试外，有时候（比如开发复杂的Servlets的时候），你需要对Servlet本身的代码块进行测试，这时候你可以选择HttpUnit，它可以提供一个模拟的Servlet容器，让你的Servlet代码不需要发布到Servlet容器（如tomcat）就可以直接测试。 
<P class=normal><B>5.2.1&nbsp;&nbsp;原理简介</B></P>
<P class=normal>使用httpunit测试Servlet时，请创建一个ServletRunner的实例，他负责模拟Servlet容器环境。如果你只是测试一个Servlet,你可以直接使用registerServlet方法注册这个Servlet，如果需要配置多个Servlet，你可以编写自己的web.xml，然后在初始化ServletRunner的时候将它的位置作为参数传给ServletRunner的构造器。</P>
<P class=normal>在测试Servlet时，应该记得使用ServletUnitClient类作为客户端，他和前面用过的WebConversation差不多，都继承自WebClient，所以他们的调用方式基本一致。要注意的差别是，在使用ServletUnitClient时，他会忽略URL中的主机地址信息，而是直接指向他的ServletRunner实现的模拟环境。</P>
<P class=normal><B>5.2.2&nbsp;&nbsp;简单测试</B></P>
<P class=normal>本实例只是演示如何简单的访问Servlet并且获取他的输出信息，例子中的Servlet在接到用户请求的时候只是返回一串简单的字符串：Hello World!. 
<P class=normal>1. Servlet的代码如下： <PRE>public class MyServlet extends HttpServlet {

 public void service(HttpServletRequest req, HttpServletResponse resp)
 throws IOException
 {
  PrintWriter out = resp.getWriter();
  //向浏览器中写一个字符串Hello World!
  out.println("<HTML>Hello World!");
  out.close();
 } 
          
}
</PRE>
<P class=normal>2. 测试的调用代码如下： <PRE>//创建Servlet的运行环境
ServletRunner sr = new ServletRunner();
//向环境中注册Servlet
sr.registerServlet( "myServlet", MyServlet.class.getName() );
//创建访问Servlet的客户端
ServletUnitClient sc = sr.newClient();
//发送请求
WebRequest request   = new GetMethodWebRequest( "http://localhost/myServlet" );
//获得模拟服务器的信息
WebResponse response = sc.getResponse( request );
//将获得的结果打印到控制台上
System.out.println(response.getText());
</PRE>
<P class=normal><B>5.2.3&nbsp;&nbsp;测试Servlet的内部行为</B></P>
<P class=normal>对于开发者来说，仅仅测试请求和返回信息是不够的，所以HttpUnit提供的ServletRunner模拟器可以让你对被调用Servlet内部的行为进行测试。和简单测试中不同，这里使用了InvocationContext获得该Servlet的环境，然后你可以通过InvocationContext对象针对request、response等对象或者是该Servlet的内部行为（非服务方法）进行操作。</P>
<P class=normal>下面的代码演示了如何使用HttpUnit模拟Servlet容器，并且通过InvocationContext对象，测试Servlet内部行为的大部分工作，比如控制request、session、response等。</P><PRE>//创建Servlet的运行环境
ServletRunner sr = new ServletRunner();
//向环境中注册Servlet
sr.registerServlet( "InternalServlet", InternalServlet.class.getName() );
//创建访问Servlet的客户端
ServletUnitClient sc = sr.newClient();

//发送请求
WebRequest request   = new GetMethodWebRequest( "http://localhost/InternalServlet" );
request.setParameter("pwd","pwd");
//获得该请求的上下文环境
InvocationContext ic = sc.newInvocation( request );
        
//调用Servlet的非服务方法
InternalServlet is = (InternalServlet)ic.getServlet();
is.myMethod();
     
//直接通过上下文获得request对象
System.out.println("request中获取的内容："+ic.getRequest().getParameter("pwd"));
     
//直接通过上下文获得response对象,并且向客户端输出信息
ic.getResponse().getWriter().write("haha");
     
//直接通过上下文获得session对象，控制session对象
//给session赋值
ic.getRequest().getSession().setAttribute("username","timeson");
//获取session的值
System.out.println("session中的值："+ic.getRequest().getSession().getAttribute("username"));
          
//使用客户端获取返回信息，并且打印出来
WebResponse response = ic.getServletResponse();
System.out.println(response.getText());
</PRE>
<P class=normal><B>[注意]</B></P>
<P class=normal>在测试Servlet的之前，你必须通过InvocationContext完成Servlet中的service方法中完成的工作，因为通过newInvocation方法获取InvocationContext实例的时候该方法并没有被调用。</P>
<P><B>6&nbsp;&nbsp;总结</B></P>
<P class=normal>本文中，作者详细的演示和介绍了如何使用HttpUnit提供的类来进行集成测试，主要实现以下操作：</P>
<OL>
<LI>模拟用户行为向服务器发送请求，传递参数 
<LI>模拟用户接受服务器的响应信息，并且通过辅助类分析这些响应信息，结合JUnit框架进行测试 
<LI>使用HttpUnit提供的模拟Servler容器,测试开发中的Servlet的内部行为 </LI></OL>
<P><B>参考资料</B></P>
<OL>
<LI>HttpUnit帮助&nbsp;&nbsp;<A href="http://httpunit.sourceforge.net/"><FONT color=#9966cc>http://httpunit.sourceforge.net</FONT></A> 
<LI>JUnit帮助&nbsp;&nbsp;<A href="http://junit.org/index.htm"><FONT color=#594fbf>http://junit.org/index.htm</FONT></A> </LI></OL><img src ="http://www.blogjava.net/relax/aggbug/743.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/relax/" target="_blank">轻松</a> 2005-01-27 09:56 <a href="http://www.blogjava.net/relax/archive/2005/01/27/743.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 如何使用Junit编写和组织测试程序</title><link>http://www.blogjava.net/relax/archive/2005/01/21/555.html</link><dc:creator>轻松</dc:creator><author>轻松</author><pubDate>Fri, 21 Jan 2005 09:30:00 GMT</pubDate><guid>http://www.blogjava.net/relax/archive/2005/01/21/555.html</guid><wfw:comment>http://www.blogjava.net/relax/comments/555.html</wfw:comment><comments>http://www.blogjava.net/relax/archive/2005/01/21/555.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/relax/comments/commentRss/555.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/relax/services/trackbacks/555.html</trackback:ping><description><![CDATA[<DIV align=center><FONT face=宋体 size=4>Junit 教程(Kent Beck, Erich Gamma)--<A href="mailto:heshuhua@263.net">Fanto</A>译</FONT></DIV>
<P>以下是一份简单的教程，向您展示如何使用Junit编写和组织测试程序。<BR></P>
<P><B><FONT size=6>一个简单的测试用例。</FONT></B></P>
<P>您是怎样编写测试代码的呢？</P>
<P>在调试器中使用表达式也许是最简单的办法。您可以不用重新编译，就能改变调试器中的表达式，您甚至可以在您看到运行的对象实例后再决定如何改变；您也可以写一些作为测试的表达式将结果打印到标准输出。以上风格的测试都有一些局限，因为它们都需要人为的判断来分析程序运行的结果；而且，呈现给您的也是一些不友好的输出。您每次只能运行一个调试表达式，如果一个程序有太多的输出语句将导致您很难找到您需要的结果。</P>
<P>JUnit Test不需要人的判断去解释，而且一次可以运行很多的测试。如果您需要测试某个东东的时候，您只要这么做即可：</P>
<OL>
<LI>从TestCase继承出一个子类。 
<LI>重写runTest()方法。 
<LI>当您想检查一个值时，调用assertTrue()方法，并传入一个布尔量真值来代表测试通过。 </LI></OL>
<P>例如：为了测试同一货币单位的两个钱数的和，我们包含了一个真实的值表示了这两个钱数的和。如下：</P>
<P>public void testSimpleAdd()</P>
<P>{</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Money m12CHF = new Money(12,"CHF");</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Money m14CHF = new Money(14,"CHF");</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Money expected= new Money(26,"CHF");</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Money result = m12CHF.add(m14CHF)</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;assertTrue(expected.equals(result));</P>
<P>}</P>
<P>如果，您要写的测试与以前写过的有些类似，那就写一个模板。如果，您想运行多个测试，那就建立一个组。</P>
<P>&nbsp;</P>
<P><B><FONT size=6>模板</FONT></B>：</P>
<P>当您有两个或多个测试需要操作对象的同一或相近部分，该怎么办？</P>
<P>测试需要运行在部分内容已经确定的对象上，这些已知的部分被称作测试模板。当您在写测试的时候，您通常会发现您构建测试环境（已知部分）的时间要比您真正比较测试结果的时间要长。</P>
<P>从某种程度上说，您如果仔细使用构造函数，您写模板的时候也许更容易些。不管怎么样，许多的保存内容来自共享的模板。通常，您能够将这个模板应用到一些不同的测试上。每个测试用例将传递相近的信息或参数给模板，然后检查不同的结果。</P>
<P>当您写一个通用的模板时，下面是您所要做的：</P>
<OL>
<LI>从TestCase生成子类。 
<LI>给模板添加需要使用的实例变量。 
<LI>覆写setUp()方法来实例化变量。 
<LI>覆写tearDown()方法来释放您在setUp()方法中建立的永久资源。 </LI></OL>
<P>例如：为了写一些使用到 12瑞士法郎，14瑞士法郎，28美元不同组合的测试用例，那就首先写一个模板：</P>
<P>public class MoneyTest extends TestCase</P>
<P>{</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;private Money f12CHF;</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;private Money f14CHF;</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;private Money f28USD;</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;protected void setUp()</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;f12CHF = new Money(12,"CHF");</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;f14CHF = new Money(14,"CHF");</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;f28USD = new Money(28,"USD");</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</P>
<P>}</P>
<P>一旦您写完了模板，那么，您就可以再写随意多的测试用例了。</P>
<P><B><FONT size=6>测试用例</FONT></B></P>
<P>当您拥有了模板后，您是怎样来写和调用单独的测试用例呢？</P>
<P>当没有模板的时候，写测试用例是简单的--只需覆写TestCase的匿名子类中的runTest方法。有模板后，生成TestCase的子类来写设置的代码。然后，为单独的测试用例写匿名子类。然而，当写过一些测试以后，您将注意到，很多的代码行都浪费在语法上了。</P>
<P>JUnit提供了一个简练的方法来利用模板写测试，如下：</P>
<P>1，在包含模板的类中提供一个public void 方法，通常约定：方法名以test开头。</P>
<P>例如：为了测试Moeny 和MoneyBag的和，如下：</P>
<P>public void testMoneyMoneyBag()</P>
<P>{</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//[12 CHF] +[14 CHF] +[28 USD] == {[26 CHF] [28 USD] }</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Money bag[] = {f26CHF,f28USD};</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;MoneyBag expected = new MoneyBag(bag);</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;assertEquals(expected,f12CHF.add(f28USD.add(f14CHF)));</P>
<P>}</P>
<P>创建一个MoneyTest实例来运行这个用例的方法，如下：</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;new MoneyTest("testMoneyMoneyBag")</P>
<P>当这个测试运行时，这个参数名字被用来查找需要运行的方法。</P>
<P>当您有多个测试用例时，可以将他们组织成(套件)suite.</P>
<P><FONT size=6><B>套件（suite）</B></FONT></P>
<P>您怎样才能一次运行多个测试？</P>
<P>只要您有了两个测试，您可能就希望一起运行他们。您当然可以每次只运行一个，但是很快您就会感到厌倦。JUnit提供了一个对象，TestSuite，以方便您一次完成任意多的测试一起运行。</P>
<P>例如：只运行一个测试用例，您可能会执行：</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TestResult result = (new MoneyTest("testMoneyMoneyBag")).run();</P>
<P>运行两个测试用例，可以先产生一个套件(Suite),然后将这两个测试用例包含其中，如下：</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TestSuite suite = new TestSuite();</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;suite.addTest(new MoneyTest("testMoneyMoneyBag"));</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;suite.addTest(new MoneyTest("testSimpleAdd"));</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TestResult result = suite.run();</P>
<P>您可以采取另外的一种方式来一次运行多个测试用例，那就是让JUnit自己从用例类(TestCase)中提取套件(Suite)。您可以通过将用例类（TestCase）的类名传递给套件(Suite)的构造函数来做到这点。<FONT size=6></FONT></P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TestSuite suite = new TestSuite(MoneyTest.class);</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TestResult result = suite.run();</P>
<P>使用手工方法的多数情况是，我们希望套件中只包含测试用例的一个子集。其他情况，推荐使用自动提取测试套件方法，它能够避免当您在新添加了一个测试用例后，还需要更改TestSuite(套件)产生代码。<FONT size=6></FONT></P>
<P>TestSuites(套件)不仅可以包含测试用例，它还可以包含实现Test接口的任意对象。例如，您可以在您的代码中产生一个套件，同时，我也产生一个，然后我们可以产生一个包含上述两个套件的套件来一起运行。</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TestSuite suite = new TestSuite();</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;suite.addTest(Kent.suite());</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;suite.addTest(Erich.suite());</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TestResult result = suite.run();</P>
<P><FONT size=6><B>TestRunner(测试执行器)</B></FONT></P>
<P>您怎样运行测试，并收集执行后的结果？</P>
<P>当您有了一个测试套件的时候，您就想运行它。Juint提供了工具来定义这个套件运行并显示测试结果，您需使您的套件能被TestRunner(测试运行器)访问；您可以使用静态方法suite()，并且返回一个suite(套件)来完成这项工作。</P>
<P>public static Test suite()</P>
<P>{</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TestSuite suite = new TestSuite();</P>
<P>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;suite.addTest(new MoneyTest("testMoneyEquals")); <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; suite.addTest(new MoneyTest("testSimpleAdd")); <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return suite;<BR></P>
<P>}</P>
<P>或则，采用自动提取的方式：</P>
<P>public static Test suite() { <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new TestSuite(MoneyTest.class); <BR>}</P>
<P>如果，TestCase没有定义suite方法，测试执行器将自动尝试提取一个suite,并把以test开头的方法装入套件。</P>
<P>Juint提供了图形和文本两种方式的测试执行器，启动方式分别是：java junit.awtui.TestRunner 或则 java junit.swingui.TestRunner.</P>
<P>图形界面的执行方式提供了一个窗口，内容包括：</P>
<OL>
<LI>一个输入文本框，用来键入包含suite方法的类的名字。 
<LI>一个启动测试的按钮。 
<LI>一个进度条，如果测试失败，他将从绿色变为红色。 
<LI>一个包含失败的测试的列表。 </LI></OL>
<P>当测试不通过时，juint在底部提供一个失败测试的报表。juint区分失败和错误。失败是预期的，并且使用断言assertions来做检查的.错误是没有预计到的，象数组索引越界。下图包含了一个失败的测试。</P>
<P align=center>&nbsp;</P>
<P>&nbsp;</P>
<P>当您改变代码后，您必须重新启动图形界面窗口，这是繁琐和耗时的。为了避免这种情况，JUnit 的AWT 和Swing UIs 可以利用junit.runner.LoadingTestCollector ，这个工具在测试的每次运行时都重新读入您的类。如果您想关闭这个功能，去掉“reload classes”属性选项即可。在帮助中您可以找到更详细的信息。</P>
<P>有一个批处理来启动Junit.您可以在命令行中键入java junit.textui.TestRunner ，后跟包含suite方法的类名。这个方式得到一些终端文本输出。另外一种启动的方式是在您的TestCase类中，包含如下定义的main方法。</P>
<P>例如：启动MoneyTest的测试执行器，</P>
<P>public static void main(String args[]) { <BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; junit.textui.TestRunner.run(suite());<BR>}<BR></P>
<P>当您定义了这个main方法后，您就可以在命令行中键入：java MoneyTest 来运行您的测试了。</P>
<P>无论是图形方式还是文本方式，都要确认在您的classpath上是否包含了junit.jar.<BR></P>
<P>&nbsp;</P>
<P>在本文中，TestCase--测试用例，Suite--套件，TestRunner--测试执行器，————译者</P><img src ="http://www.blogjava.net/relax/aggbug/555.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/relax/" target="_blank">轻松</a> 2005-01-21 17:30 <a href="http://www.blogjava.net/relax/archive/2005/01/21/555.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>循序渐进学习JUnit </title><link>http://www.blogjava.net/relax/archive/2005/01/21/550.html</link><dc:creator>轻松</dc:creator><author>轻松</author><pubDate>Fri, 21 Jan 2005 08:35:00 GMT</pubDate><guid>http://www.blogjava.net/relax/archive/2005/01/21/550.html</guid><wfw:comment>http://www.blogjava.net/relax/comments/550.html</wfw:comment><comments>http://www.blogjava.net/relax/archive/2005/01/21/550.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/relax/comments/commentRss/550.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/relax/services/trackbacks/550.html</trackback:ping><description><![CDATA[<P><SPAN class=italicbodycopy><EM><FONT face=Arial size=2>作者：Michel Casabianca</FONT></EM></SPAN> </P>
<P><SPAN class=boldbodycopy><I><STRONG><FONT face=Arial size=2>使用最流行的开放资源测试框架之一学习单元测试基础。</FONT></STRONG></I></SPAN> </P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>使用JUnit可以大量减少Java代码中程序错误的个数，JUnit是一种流行的单元测试框架，用于在发布代码之前对其进行单元测试。现在让我们来详细研究如何使用诸如JUnit、Ant和Oracle9i JDeveloper等工具来编写和运行单元测试。</FONT></SPAN> </P>
<P><SPAN class=parahead1><STRONG><FONT face=Arial>为什么使用JUnit？</FONT></STRONG></SPAN></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>多数开发人员都同意在发布代码之前应当对其进行测试，并利用工具进行回归（regression）测试。做这项工作的一个简单方法是在所有Java类中以main()方法实施测试。例如，假设使用ISO格式（这意味着有一个以这一格式作为参数的构造器和返回一个格式化的ISO字符串的toString()方法）以及一个GMT时区来编写一个Date的子类。</FONT><A href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit_l1.html" target=_top><SPAN class=bodylink><FONT face=Arial color=#000000 size=2>清单1</FONT></SPAN></A></SPAN><SPAN class=bodycopy><FONT face=Arial size=2> 就是这个类的一个简单实现。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>不过，这种测试方法并不需要单元测试限定语（qualifier），原因如下：</FONT></SPAN></P>
<P><FONT face=Arial size=2></FONT>
<UL>
<LI><SPAN class=bulletbodycopy>在一个类中进行测试的最小单元是方法，你应当对每个方法进行单独测试，以准确地找出哪些方法工作正常，哪些方法工作不正常。</SPAN> 
<LI><SPAN class=bulletbodycopy>即使前面的测试失败，也应当对各个方法进行测试。在此实施中，如果单个测试失败，后面的测试将根本不会运行。这就意味着你不会知道不良代码在你的实施中所占的百分比。</SPAN> 
<LI><SPAN class=bulletbodycopy>测试代码会出现在生成的类中。这在类的大小方面可能不是什么问题，但却可能会成为安全性因素之一：例如，如果你的测试嵌入了数据库连接密码，那么这一信息将很容易用于已发布的类中。</SPAN> 
<LI><SPAN class=bulletbodycopy>没有框架可以自动启动这一测试，你必须编写一个脚本来启动每一个测试。</SPAN> 
<LI><SPAN class=bulletbodycopy>在编写一个报告时，你必须编写自己的实现，并定义规则，以方便地报告错误。</SPAN> </LI></UL>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>JUnit框架就是设计用来解决这些问题的。这一框架主要是所有测试实例（称为"TestCase"）的一个父类，并提供工具来运行所编写的测试、生成报告及定义测试包（test suite）。</FONT></SPAN> </P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>让我们为</FONT></SPAN><TT>IsoDate</TT><SPAN class=bodycopy><FONT face=Arial size=2>类编写一个测试：这个</FONT></SPAN><TT>IsoDateTest</TT><SPAN class=bodycopy><FONT face=Arial size=2>类类似于： </FONT></SPAN></P>
<P><FONT face=Arial size=2></FONT><PRE>import java.text.ParseException;
import junit.framework.TestCase;


/**
 * Test case for &lt;code&gt;IsoDate&lt;/code&gt;.
 */
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());
  }
}
</PRE>
<P><SPAN class=bodycopy><FONT face=Arial size=2>本例中要注意的重点是已经编写了一个用于测试的独立类，因此可以对这些文件进行过滤，以避免将这一代码嵌入到将要发布的文档中。另外，本例还为你希望在你的代码中测试的每个方法编写了一个专用测试方法，因此你将确切地知道需要对哪些方法进行测试、哪些方法工作正常以及哪些方法工作不正常。如果在编写实施文档之前已经编写了该测试，你就可以利用它来衡量工作的进展情况。</FONT></SPAN></P>
<P><SPAN class=parahead1><STRONG><FONT face=Arial>安装并运行JUnit</FONT></STRONG></SPAN></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>要运行此示例测试实例，必须首先下载并安装JUnit。JUnit的最新版本可以在JUnit的网站</FONT><A href="http://www.junit.org/" target=_blank><SPAN class=bodylink><FONT face=Arial color=#000000 size=2> www.junit.org</FONT></SPAN></A><FONT face=Arial size=2>免费下载。该软件包很小（约400KB），但其中包括了源代码和文档。要安装此程序，应首先对该软件包进行解压缩（junitxxx.zip）。它将创建一个目录（junitxxx），在此目录下有文档（在doc目录中）、框架的应用编程接口（API）文档（在javadoc目录中）、运行程序的库文件（junit.jar）以及示例测试实例（在junit目录中）。截至我撰写本文时，JUnit的最新版本为3.8.1，我是在此版本上对示例进行测试的。</FONT></SPAN> </P>
<P><A name=f1>
<TABLE width="100%" align=center>
<TBODY>
<TR>
<TD align=middle><IMG height=160 alt="IsoDate Test" src="http://otn.oracle.com/oramag/oracle/03-may/images/o33junit_f1.jpg" width=450 border=0> </TD></TR></TBODY></TABLE></A>
<P></P>
<P>
<CENTER><SPAN class=bodycopy><FONT face=Arial size=2>图1 运行IsoDate测试 </FONT></SPAN></CENTER>
<P><FONT face=Arial size=2></FONT></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>要运行此测试实例，将源文件（</FONT><A href="http://otn.oracle.com/oramag/oracle/03-may/IsoDate.java" target=_top><SPAN class=bodylink><FONT face=Arial color=#000000 size=2>IsoDate.java</FONT></SPAN></A><FONT face=Arial size=2>和</FONT><A href="http://otn.oracle.com/oramag/oracle/03-may/IsoDateTest.java" target=_top><SPAN class=bodylink><FONT face=Arial color=#000000 size=2>IsoDateTest.java</FONT></SPAN></A><FONT face=Arial size=2>）拷贝到Junit的安装目录下，打开终端，进入该目录，然后输入以下命令行（如果你正在使用UNIX）：</FONT></SPAN> </P>
<P><PRE>export CLASSPATH=.:./junit.jar
javac *.java
或者，如果你正在Windows，输入以下命令行

set CLASSPATH=.;junit.jar
javac *.java
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>这些命令行对CLASSPATH进行设置，使其包含当前目录中的类和junit.jar库，并编译Java源文件。</FONT></SPAN></P>
<P><FONT size=2><FONT face=Arial><SPAN class=bodycopy>要在终端上运行该测试，输入以下命令行：</SPAN><BR></FONT></FONT></P>
<P><FONT face=Arial size=2></FONT><PRE>java junit.textui.TestRunner IsoDateTest
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>此命令行将运行该测试，并在</FONT><A href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit.html#f1"><SPAN class=bodylink><FONT face=Arial color=#000000 size=2>图 1</FONT></SPAN></A></SPAN><SPAN class=bodycopy><FONT face=Arial size=2>所示的控制台上显示测试结果。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>才在此工具可以运行类名被传递到命令行中的单个测试。注意：只有对命令行的最后测试才在考虑之内，以前的测试都被忽略了。（看起来像一个程序错误，是吧？）</FONT></SPAN></P>
<P><FONT size=2><FONT face=Arial><SPAN class=bodycopy>JUnit还提供了利用AWT（抽象窗口工具包）或Swing运行测试的图形界面。为了利用此图形界面运行测试，在终端上输入以下命令行：</SPAN><BR></FONT></FONT></P>
<P><FONT face=Arial size=2></FONT><PRE>java junit.awtui.TestRunner IsoDateTest
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>或者使用Swing界面： </FONT></SPAN></P>
<P><FONT face=Arial size=2></FONT><PRE>java junit.swingui.TestRunner IsoDateTest
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>此命令行将显示</FONT><A href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit.html#f2"><SPAN class=bodylink><FONT face=Arial color=#000000 size=2>图 2</FONT></SPAN></A></SPAN><SPAN class=bodycopy><FONT face=Arial size=2>所示的界面。要选择一个测试并使其运行，点击带有三个点的按钮。这将显示CLASSPATH（还有测试包，但我们将在后面讨论）中所有测试的列表。要运行测试，点击"Run"按钮。测试应当正确运行，并在</FONT><A href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit.html#f2"><SPAN class=bodylink><FONT face=Arial color=#000000 size=2>图 2</FONT></SPAN></A><FONT face=Arial size=2>所示的界面中显示结果。</FONT></SPAN> </P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>在此界面中你应当选中复选框"Reload Classes Every Run"，以便运行器在运行测试类之前对它们进行重新加载。这样就可以方便地编辑、编译并运行测试，而不需要每次都启动图形界面。</FONT></SPAN></P>
<P><FONT size=2><FONT face=Arial><SPAN class=bodycopy>在该复选框下面是一个进度条，在运行较大的测试包时，该进度条非常有用。运行的测试、错误和失败的数量都会在进度条下面显示出来。再下面是一个失败列表和一个测试层次结构。失败消息显示在底部。通过点击Test Hierarchy（测试层次结构）面板，然后再点击窗口右上角的"Run"按钮，即可运行单个测试方法。请记住，使用命令行工具是不可能做到这些的。</SPAN><BR></FONT></FONT></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>注意，当运行工具来启动测试类时，这些类必须存在于CLASSPATH中。但是如果测试类存储在jar文件中，那么即使这些jar文件存在于CLASSPATH中，JUnit也不能找到这些测试类。</FONT></SPAN></P>
<P><A name=f2>
<TABLE width="100%" align=center>
<TBODY>
<TR>
<TD align=middle><FONT face=Arial size=2><IMG height=396 alt="Swing interface" src="http://otn.oracle.com/oramag/oracle/03-may/images/o33junit_f2.jpg" width=400 border=0></FONT> </TD></TR></TBODY></TABLE></A>
<P></P>
<P>
<CENTER><SPAN class=bodycopy><FONT face=Arial size=2>图2 用于运行测试的Swing界面</FONT></SPAN> </CENTER>
<P></P>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>这并不是一种启动测试的方便方法，但幸运的是，JUnit已经被集成到了其他工具（如Ant和Oracle9i JDeveloper）中，以帮助你开发测试并使测试能够自动运行。</FONT></SPAN></P>
<P><SPAN class=parahead1><STRONG><FONT face=Arial>编写Junit测试实例</FONT></STRONG></SPAN></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>你已经看到了测试类的源代码对IsoDate实施进行了询问。现在让我们来研究这样的测试文件的实施。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>测试实例由junit.frameword.TestCase继承而来是为了利用JUnit框架的优点。这个类的名字就是在被测试类的名字上附加"Test"。因为你正在测试一个名为IsoDate的类，所以其测试类的名字就是IsoDateTest。为了访问除私有方法之外的所有方法，这个类通常与被测类在同一个包中。<BR></FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>注意，你必须为你希望测试的在类中定义的每个方法都编写一个方法。你要测试构造器或使用了ISO日期格式的方法，因此你将需要为以ISO格式的字符串作为参数的构造器和toString()方法编写一个测试方法。其命名方式与测试类的命名方式类似：在被测试方法（或构造器）前面附加"test"。 </FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>测试方法的主体通过验证assertion（断言）对被测方法进行询问。例如，在toString()实施的测试方法中，你希望确认该方法已经对时间的设定进行了很好的说明（对于UNIX系统来说，最初问世的时间为1970年1月1日的午夜）。要实施assertion，你可以使用Junit框架提供的assertion方法。这些方法在该框架的junit.framework.Assert类中被实施，并且可以在你的测试中被访问，这是因为Assert是TestCase的父类。这些方法可与Java中的关键字assert（是在J2EE 1.4中新出现的）相比。一些assertion方法可以检查原始类型（如布尔型、整型等）之间或对象之间是否相等（利用equals()方法检查两个对象是否相等）。其他assertion方法检查两个对象是否相同、一个对象是否为"空"或"非空"，以及一个布尔值（通常由一个表达式生成）是"真"还是"假"。在</FONT><A href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit_2.html#t1"><SPAN class=bodylink><FONT face=Arial color=#000000 size=2>表 1</FONT></SPAN></A><FONT face=Arial size=2>中对这些方法进行了总结。</FONT></SPAN> </P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>对于那些采用浮点类型或双精度类型参数的assertion，存在一个第三种方法，即采用一个delta值作为参数进行比较。另外还要注意，assertEquals()和assertSame()方法一般不会产生相同的结果。（两个具有相同值的字符串可以不相同，因为它们是两个具有不同内存地址的不同对象。）因此，assertEquals()将会验证assertion的有效性，而assertSame()则不会。注意，对于</FONT><A href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit_2.html#t1"><SPAN class=bodylink><FONT face=Arial color=#000000 size=2>表 1</FONT></SPAN></A></SPAN> <SPAN class=bodycopy><FONT face=Arial size=2>中的每个assertion方法，你还有一种选择，就是引入另一个参数，如果assertion失败，该参数就会给出一条解释性消息。例如，assertEquals（int 期望值, int 实际值）就可以与一个诸如assertEquals（字符串消息，int期望值，int实际值）的消息一起使用。 </FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>当一个assertion失败时，该assertion方法会抛出一个AssertFailedError或ComparisonFailure。AssertionFailedError由java.lang.Error继承而来，因此你不必在测试方法的throws语句中对其进行声明。而ComparisonFailure由AssertionFailedError继承而来，因此你也不必对其进行声明。因为当一个assertion失败时会在测试方法中抛出一个错误，所以后面的assertion将不会继续运行。框架捕捉到这些错误并认定该测试已经失败后，就会打印出一条说明错误的消息。这个消息由assertion生成，并且被传递到assertion方法（如果有的话）。</FONT></SPAN></P>
<P><FONT size=2><FONT face=Arial><SPAN class=bodycopy>现在将下面一行语句添加到testIsoDate()方法的末尾：</SPAN><BR></FONT></FONT></P>
<P><FONT face=Arial size=2></FONT><PRE>assertEquals("This is a test",1,2);
</PRE>
<P><SPAN class=bodycopy><FONT face=Arial size=2>现在编译并运行测试：</FONT></SPAN></P>
<P><FONT face=Arial size=2></FONT><PRE>$ 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:&lt;1&gt; but was:&lt;2&gt;
      at IsoDateTest.testIsoDate
      (IsoDateTest.java:29)

FAILURES!!!
Tests run: 2,  Failures: 1,  Errors: 0
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>JUnit为每个已处理的测试打印一个点，显示字母"F"来表示失败，并在assertion失败时显示一条消息。此消息由你发送到assertion方法的注释和assertion的结果组成（自动生成）。从这里可以看出assertion方法的参数顺序对于生成的消息非常重要。第一个参数是期望值，而第二个参数则是实际值。</FONT></SPAN></P>
<P><FONT size=2><FONT face=Arial><SPAN class=bodycopy>如果在测试方法中出现了某种错误（例如，抛出了一个异常），该工具就会将其显示为一个错误（而不是由assertion失败而产生的一个"失败"）。现在对IsoDateTest类进行修改，以将前面增加的一行语句用以下语句代替：</SPAN><BR></FONT></FONT></P>
<P><FONT face=Arial size=2></FONT><PRE>throw new Exception("This is a test"); 
</PRE>
<P><SPAN class=bodycopy><FONT face=Arial size=2>然后编译并运行测试：</FONT></SPAN></P>
<P><FONT face=Arial size=2></FONT><PRE>$ javac *.java
$ java junit.textui.TestRunner IsoDateTest 
.E.
Time: 0,284
There was 1 error:
1) testIsoDate(IsoDateTest)java.lang.
   Exception: This is a test at IsoDate
   Test.testIsoDate(IsoDateTest.java:30)

FAILURES!!!
Tests run: 2,  Failures: 0,  Errors: 1
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>该工具将该异常显示为一个错误。因此，一个错误表示一个错误的测试方法，而不是表示一个错误的测试实施。</FONT></SPAN></P>
<P><FONT size=2><FONT face=Arial><SPAN class=bodycopy>Assert类还包括一个fail()方法（该版本带有解释性消息），该方法将通过抛出AssertionFailedError来中断正在运行的测试。当你希望一个测试失败而不会调用一个判定方法时，fail()方法是非常有用的。例如，如果一段代码应当抛出一个异常而未抛出，那么可以调用fail()方法使该测试失败，方法如下：</SPAN><BR></FONT></FONT></P>
<P><FONT face=Arial size=2></FONT><PRE>public void testIndexOutOfBounds() {
  try {
       ArrayList list=new ArrayList();
       list.get(0);
       fail("IndexOutOfBoundsException   
           not thrown");
  } catch(IndexOutOfBoundsException e) {}
}
</PRE>
<P></P>
<P><SPAN class=parahead1><STRONG><FONT face=Arial>JUnit的高级特性</FONT></STRONG></SPAN></P>
<P><FONT size=2><FONT face=Arial><SPAN class=bodycopy>在示例测试实例中，你已经同时运行了所有的测试。在现实中，你可能希望运行一个给定的测试方法来询问你正编写的实施方法，所以你需要定义一组要运行的测试。这就是框架的junit.framework.TestSuite类的目的，这个类其实只是一个容器，你可以向其中添加一系列测试。如果你正在进行toString()实施，并希望运行相应的测试方法，那么你可以通过重写测试的suite()方法来通知运行器，方法如下：</SPAN><BR></FONT></FONT></P>
<P><FONT face=Arial size=2></FONT><PRE>public static Test suite() {

  TestSuite suite= new TestSuite();
  suite.addTest(new IsoDateTest
("testToString"));
  return suite;
}
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>在此方法中，你用具体示例说明了一个TestSuite对象，并向其中添加了测试。为了在方法级定义测试，你可以利用构造器将方法名作为参数使测试类实例化。此构造器可按如下方法实施：</FONT></SPAN></P>
<P><FONT face=Arial size=2></FONT><PRE>public IsoDateTest(String name) {
  super(name);
}
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>将上面的构造器和方法添加到IsoDateTest类（还需要引入junit.framework.Test和junit.framework.TestSuite），并在终端上输入： </FONT></SPAN></P>
<P><A name=f3>
<TABLE width="100%" align=center>
<TBODY>
<TR>
<TD align=middle><FONT face=Arial size=2><IMG height=144 alt="selecting a test method" src="http://otn.oracle.com/oramag/oracle/03-may/images/o33junit_f3.jpg" width=400 border=0></FONT> </TD></TR></TBODY></TABLE></A>
<CENTER>
<P><SPAN class=bodycopy><FONT face=Arial size=2>图3：选择一个测试方法 </FONT></SPAN></P></CENTER>
<P><FONT face=Arial size=2></FONT>&nbsp;</P>
<P><FONT face=Arial size=2></FONT><PRE> 
$ javac *.java
$ java junit.textui.TestRunner IsoDateTest
.
Time: 0,31
OK (1 test)
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>注意，在添加到测试包中的测试方法中，只运行了一个测试方法，即toString()方法。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>你也可以利用图形界面，通过在图3所示的Test Hierarchy面板中选择测试方法来运行一个给定的测试方法。但是，要注意当整个测试包被运行一次后，该面板将被填满。</FONT></SPAN></P>
<P><FONT size=2><FONT face=Arial><SPAN class=bodycopy>当你希望将一个测试实例中的所有测试方法添加到一个TestSuite对象中时，可以使用一个专用构造器，该构造器将此测试实例的类对象作为参数。例如，你可以使用IsoDateTest类实施suite()方法，方法如下：</SPAN><BR></FONT></FONT></P>
<P><FONT face=Arial size=2></FONT><PRE>public static Test suite() {
  return new TestSuite(IsoDateTest.class);
}
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>还有一些情况，你可能希望运行一组由其他测试（如在工程发布之前的所有测试）组成的测试。在这种情况下，你必须编写一个实施suite()方法的类，以建立希望运行的测试包。例如，假定你已经编写了测试类Atest和Btest。为了定义那些包含了类ATest中的所有测试和在BTest中定义的测试包的集合，可以编写下面的类：</FONT></SPAN></P>
<P><FONT face=Arial size=2></FONT><PRE>import junit.framework.*;

/**
 * TestSuite that runs all tests.
 */
public class AllTests {

  public static Test suite() {
     TestSuite suite= new TestSuite("All Tests");
     suite.addTestSuite(ATest.class);
     suite.addTest(BTest.suite());
     return suite;
  }
}
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>你完全可以像运行单个测试实例那样运行这个测试包。注意，如果一个测试在一个套件中添加了两次，那么运行器将运行它两次（测试包和运行器都不会检查该测试是否是唯一的）。为了了解实际的测试包的实施，应当研究Junit本身的测试包。这些类的源代码存在于JUnit安装的junit/test目录下。</FONT></SPAN></P>
<P><A name=f4>
<TABLE width="100%" align=center>
<TBODY>
<TR>
<TD align=middle><FONT face=Arial size=2><IMG height=272 alt="test results" src="http://otn.oracle.com/oramag/oracle/03-may/images/o33junit_f4.jpg" width=400 border=0></FONT> </TD></TR></TBODY></TABLE></A>
<P></P>
<P>
<CENTER><SPAN class=bodycopy><FONT face=Arial size=2>图4：显示测试结果的报告 </FONT></SPAN></CENTER>
<P><FONT face=Arial size=2></FONT></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>将一个main()方法添加到一个测试或一个测试包中有时是非常方便的，因此可以在不使用运行器的情况下启动测试。例如，要将AllTests测试包作为一个标准的Java程序启动，可以将下面的main()方法添加到类中： </FONT></SPAN></P>
<P><FONT face=Arial size=2></FONT><PRE>public static void main(String[] args) {
  junit.textui.TestRunner.run(suite());
}
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>现在可以通过输入java AllTests来启动这个测试包。 </FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>JUnit框架还提供了一种有效利用代码的方法，即将资源集合到被称为fixture的对象集中。例如，该示例测试实例利用两个叫作epoch和eon的参考日期。将这些日期重新编译到每个方法测试中只是浪费时间（而且还可能出现错误）。你可以用fixture重新编写测试，如</FONT><A href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit_l2.html" target=_top><SPAN class=bodylink><FONT face=Arial color=#000000 size=2>清单2</FONT></SPAN></A><FONT face=Arial size=2>所示。</FONT></SPAN> </P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>你定义了两个参考日期，作为测试类的段，并将它们编译到一个setUp()方法中。这一方法在每个测试方法之前被调用。与其对应的方法是tearDown()方法，它将在每个测试方法运行之后清除所有的资源（在这个实施中，该方法事实上什么也没做，因为垃圾收集器为我们完成了这项工作）。现在编译这个测试实例（其源代码应当放在JUnit的安装目录中）并运行它：</FONT></SPAN></P>
<P><FONT face=Arial size=2></FONT><PRE>$ javac *.java
$ java junit.textui.TestRunner IsoDateTest2
.setUp()
testIsoDate()
tearDown()
.setUp()
testToString()
tearDown()

Time: 0,373

OK (2 tests)
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT face=Arial size=2>注意：在该测试实例中建立了参考日期，因此在任何测试方法中修改这些日期都不会对其他测试产生不利影响。你可以将代码放到这两个方法中，以建立和释放每个测试所需要的资源（如数据库连接）。</FONT></SPAN></P>
<P><FONT size=2><FONT face=Arial><SPAN class=bodycopy>JUnit发布版还提供了扩展模式（在包junit.extensions中），即test decor-ators，以提供像重复运行一个给定的测试这样的新功能。它还提供了一个TestSuite，以方便你在独立的线程中同时运行所有测试，并在所有线程中的测试都完成时停止。</SPAN><BR></FONT></FONT></P>
<P><FONT size=2><FONT face=Arial>
<TABLE cellSpacing=0 cellPadding=0 width="100%" background=/portalimages/pobtrans.gif border=0>
<TBODY>
<TR align=left>
<TD vAlign=top width="100%">
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top align=left>
<TD vAlign=top align=middle width="100%">
<TABLE cellSpacing=10 cellPadding=0 width="100%" border=0>
<TBODY>
<TR vAlign=top align=left>
<TD width="100%">
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD>
<P><SPAN class=parahead1><STRONG>利用Ant使测试自动化</STRONG></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>如前面所述，测试运行器是非常原始的。如果你正在运行Ant来编译你的工程，那么编译文件是运行单元测试的好方法。（关于Ant的介绍，请参考我的文章《Ant简介》（<SPAN class=bodycopy><A href="http://www.oracle.com/technology/oramag/oracle/02-nov/o62odev_ant.html" target=_top><SPAN class=bodylink><FONT color=#000000>Starting with Ant</FONT></SPAN></A></SPAN>），发表于Oracle杂志2002年11／12月号中）。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>假设你的源文件在src目录中，所生成的类在tmp目录中，并且junit.jar库位于工程的libdirectory目录中，那么你可以编译Java源文件，并使用</FONT><A href="http://www.oracle.com/global/cn/oramag/oracle/03-may/o33junit_l3.html" target=_top><FONT size=2>清单3</FONT></A><FONT size=2>中所示的编译文件（在工程的根目录中）运行单元测试。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>这个编译文件的核心是运行单元测试的测试目标。运行这些测试是这个目标junit的唯一任务。为了运行这一可选任务，必须首先将junit.jar库放到Ant安装目录下的lib目录中，然后下载并安装同一目录中的Ant可选任务库。</FONT><A href="http://www.oracle.com/oramag/oracle/03-may/o33junit_l3.html" target=_top><FONT size=2>清单3</FONT></A><FONT size=2>中的示例嵌套了一个classpath类，它包括JUnit库和工程的类；示例中还嵌套了一个batchtest元素，它利用一个选择适当源文件的fileset元素定义了将要运行的测试。这个任务还包括haltonfilure和haltonerror属性，它们告诉Ant在遇到一个失败或错误时是否应当停止。如果将它们的值设置为"真"，那么Ant在遇到第一个失败或错误时将会停止，编译将会失败（显然，这表示在运行测试过程中存在有问题）。另一方面，如果将它们的值设置为"假"，其结果就不是非常明确了（即使测试失败，编译也会成功），但所有测试仍将运行。printsummary属性指示Ant是否显示运行测试的输出。数值withOutAndErr可以在开发测试时方便地告诉Ant显示标准输出和错误输出。数值off表示不显示任何内容，而on只显示测试报告（没有测试类的输出）。junit任务具有很多属性，详细内容请参考Ant的文档。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>为了测试这一编译文件，你需要建立名字为src、tmp和lib的目录。将junit.jar库放到lib目录中，并将前面看到的示例Java源文件放到src目录中。打开终端，进入该工程的根目录，并输入ant，其结果为： </FONT></SPAN></P>
<P><FONT size=2></FONT><PRE>$ ant
Buildfile: build.xml

clean:
   [delete] Deleting directory 
     /Users/casa/doc/oracle

     /junit/prj/tmp
   [mkdir] Created dir: /Users/casa
     /doc/oracle/junit/prj/tmp

bin:
    [javac] Compiling 4 source files 
      to /Users/casa/doc/oracle
      /junit/prj/tmp


test:
    [junit] Running IsoDateTest
    [junit] Tests run: 1, Failures: 
      0, Errors: 0, Time elapsed: 
           0,005 sec
    [junit] Running IsoDateTest2
    [junit] Tests run: 2, Failures: 0, 
      Errors: 0, Time elapsed: 0,031 sec
    [junit] Output:
    [junit] setUp()

    [junit] testIsoDate()
    [junit] tearDown()
    [junit] setUp()
    [junit] testToString()
    [junit] tearDown()

all:

BUILD SUCCESSFUL
Total time: 8 seconds
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT size=2>Ant还可以生成非常有用的HTML格式的测试报告。为了生成这样的报告，将前面的测试目标用以下目标代替：</FONT></SPAN></P>
<P><FONT size=2></FONT><PRE>&lt;target name="test" depends="bin"
       description="Run JUnit tests"&gt;
  &lt;junit haltonfailure="false"
        printsummary="withOutAndErr"&gt;
    &lt;classpath refid="cp"/&gt;
    &lt;batchtest todir="${tmp}"&gt;
      &lt;fileset dir="${src}" 
              includes="**/*Test*.java"/&gt;
     &lt;/batchtest&gt;

     &lt;formatter type="xml"/&gt;
  &lt;/junit&gt;
  &lt;junitreport todir="${tmp}"&gt;
    &lt;fileset dir="${tmp}" 
            includes="TEST-*.xml"/&gt;
    &lt;report format="frames" 
           todir="${tmp}"/&gt;
  &lt;/junitreport&gt;
&lt;/target&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT size=2>这一目标与前面的目标相同，只是该目标在batchtext元素中增加了一个新的属性--todir，它告诉Ant在tmp目录中生成可扩展的标记语言（XML）报告。该目标还增加了一个新的junitreport元素，以便由XML文件生成一个HTML报告。这一元素要求在安装Ant的lib目录中安装Xalan库（详细内容见Ant文档的junitreport部分：</FONT><A href="http://ant.apache.org/manual/install.html" target=_blank><SPAN class=bodylink><FONT color=#000000 size=2>ant.apache.org/manual/install.html</FONT></SPAN></A><FONT size=2>）。这一元素还定义了使用todir属性生成的文件的目标目录。通过嵌套一个fileset元素来定义为生成这一报告而需要处理的XML文件。期望的输出格式利用嵌套的报告元素来实现。该对象将生成一个诸如</FONT><A href="http://www.oracle.com/oramag/oracle/03-may/o33junit.html#f4"><FONT size=2>图4</FONT></A><FONT size=2>所示的报告。 </FONT><A href="http://ant.apache.org/manual/install.html"><SPAN class=bodylink></SPAN></A></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>这类报告在使单元测试自动运行时特别有用（比如在夜间编译期间）。在这些情况下，错误或失败不会中断测试，因此你必须将前面提到的junit任务的haltonfailure和haltonerror属性设置为"假"。这一报告对于衡量实施进程也非常有用（比如当你必须重写已有代码时，或者在实施之前已经编写了测试的情况下）。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>Ant对启动JUnit图形运行器也非常有用。下面的对象将会启动Swing测试运行器：</FONT></SPAN> </P>
<P><PRE>&lt;target name="testui" depends="bin"
        description="Run graphical JUnit"&gt;
&lt;java classname="junit.swingui.TestRunner"
      classpathref="cp" 
      fork="true"/&gt;
&lt;/target&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy><FONT size=2>你应当在终端中运行这一对象，并且在另一个终端或你喜欢的IDE中使用Ant对其进行编译。这种方式使你不必在每次想要测试代码时都启动图形运行器。 </FONT></SPAN></P>
<P><SPAN class=parahead1><STRONG>在Oracle9<I>i</I> Jdeveloper中的JUnit集成</STRONG></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>Oracle9<I>i</I> JDeveloper并没有基于网络集成JUnit，但是下载并安装这一插件只需要几分钟的时间。为了完成此过程，选择JDeveloper的"Help"菜单下的"Check for Updates"项。这样将会打开IDE更新向导，以连接到Oracle技术网站，下载该插件并安装它。当安装该插件后，需要关闭并重启Oracle9<I>i</I> JDeveloper。注意，向导还会下载相关的文档。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>通过为每个任务提供向导，这个插件极大地提高了开发人员编写测试实例、测试包和fixture等的工作效率。要调用这些向导，点击"File"菜单下的"New"项，然后选择"General/Unit Tests"类，并从右侧的窗体中选择合适的向导。你也可以从界面上启动测试套件。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>当准备好对项目进行代码测试后，应当首先使用专用向导来编写fixture，然后测试实例向导可以利用它们集成到测试实例中。另外，还有一些用来生成自定义测试fixture的向导以及生成商务组件和数据库连接测试fixture的向导。这后两种向导生成专用代码，以使用setUp()和tearDown()方法设置和发布商务组件或数据库连接。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>当完成fixture后，下一步应当使用合适的向导来生成测试实例，这些向导可以让你选择要测试的类和方法。你还可以选择在这个测试中使用的fixture。这将生成一个使用测试方法的主体完成的代码框架。最后应当生成套件来运行你的测试。这个专用向导让你选择要包括在套件中的测试，并为你生成整个类。要启动一个测试套件，点击浏览器中的文件，并选择Run。这将会启动图形界面并运行套件的测试。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>在"Help"菜单中选择"Help Topics"，你将会在JDeveloper文档中找到关于如何使用这些向导的详细教程。这会打开帮助系统窗口。点击"Unit Testing with JUnit"项，然后选择合适的教程。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>JUnit和JDeveloper之间的这种集成使你能够只编写单元测试中你感兴趣的那部分的代码，而让工具为你编写重复的代码。</FONT></SPAN> 
<TABLE cellPadding=5 width="35%" align=right bgColor=#f7f7e7 border=1 vspace="5" hspace="5">
<TBODY>
<TR>
<TD>
<CENTER><SPAN class=parahead1><STRONG>下一步 </STRONG></SPAN></CENTER>
<P><STRONG><FONT size=2><SPAN class=boldbodycopy>访问JUnit网站</SPAN><BR></FONT></STRONG><A href="http://www.junit.org/" target=_blank><SPAN class=bodylink><FONT color=#000000 size=2>www.junit.org</FONT></SPAN> </A></P>
<P><A href="http://www.junit.org/" target=_blank><STRONG><FONT size=2><FONT color=#000000><SPAN class=boldbodycopy>下载</SPAN><BR><SPAN class=boldbodycopy>Oracle9<I>i</I> Jdeveloper</SPAN><BR></FONT></FONT></STRONG></A><A href="http://www.oracle.com/technology/software/products/jdev/index.html" target=_top><SPAN class=bodylink><FONT color=#000000 size=2>otn.oracle.com/software/products/jdev/</FONT></SPAN></A> </P>
<P><STRONG><FONT size=2><SPAN class=boldbodycopy>Oracle9<I>i</I>应用服务器</SPAN><BR></FONT></STRONG><A href="http://www.oracle.com/technology/software/products/ias/index.html" target=_top><SPAN class=bodylink><FONT color=#000000 size=2>otn.oracle.com/software/products/ias/</FONT></SPAN></A> </P>
<P><STRONG><FONT size=2><SPAN class=boldbodycopy>学习Oracle9<I>i</I> JDeveloper扩展</SPAN><BR></FONT></STRONG><A href="http://otn.oracle.com/products/jdev/htdocs/partners/addins" target=_top><SPAN class=bodylink><FONT color=#000000 size=2>otn.oracle.com/products/jdev/htdocs/partners/addins</FONT></SPAN></A> </P>
<P><STRONG><FONT size=2><SPAN class=boldbodycopy>阅读Oracle9<I>i</I> JDeveloper文档</SPAN><BR></FONT></STRONG><A href="http://www.oracle.com/technology/docs/products/jdev/index.html" target=_top><SPAN class=bodylink><FONT color=#000000 size=2>otn.oracle.com/docs/products/jdev/ </FONT></SPAN></A></P></TD></TR></TBODY></TABLE>
<P><FONT size=2></FONT></P>
<P><SPAN class=parahead1><STRONG>JUnit最佳实践</STRONG></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>下面是一些在使用JUnit时应当注意的技巧：</FONT></SPAN></P>
<P><FONT size=2></FONT>
<UL>
<LI><SPAN class=bulletbodycopy>在实施之前编写测试代码。这是一种合同驱动的实施方式。</SPAN> 
<LI><SPAN class=bulletbodycopy>只测试那些可能会中断的方法（也就是说，在多数情况下不应测试setter和getter方法）。要尽可能地多进行测试，以避免回归测试。当测试一个较大的应用程序时，你可以在夜间编译时运行所有测试。 </SPAN>
<LI><SPAN class=bulletbodycopy>一定要使用正确的JUnit扩展来测试特殊的应用程序（如使用Castus测试J2EE应用程序）。 </SPAN></LI></UL>
<P><SPAN class=parahead1><STRONG>值得花费的时间</STRONG></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>到现在，你应当已经清楚地知道使用JUnit框架和合适的工具实施单元测试是多么快速而简单。关于单元测试的下一个目标是使你的CTO相信你在实施测试时所必须花费的时间是为了以后节省更多的时间。但是，当你考虑在检查老代码、修正错误和发布一个调试过的版本上所花费的时间（它可能花费整个一天）时，在开发过程的早期阶段捕获的代码错误毫无疑问是一项很好的投资。这里并没有考虑当错误代码不再位于块的顶部时开发人员必须遵循的"black magic"步骤，这些步骤包括：标记代码，制作一个分支、修正代码错误、进行发布，以及将代码修正合并到块中。所有这些步骤都非常耗时，并且容易产生错误。</FONT></SPAN></P>
<P><SPAN class=bodycopy><FONT size=2>要开始使用单元测试和JUnit，请访问JUnit网站： </FONT><A href="http://www.junit.org/" target=_blank><SPAN class=bodylink><FONT color=#000000 size=2>www.junit.org</FONT></SPAN></A><FONT size=2>。你将找到大量有用的文档（包括使用JUnit实施测试的详细说明书）、一个与JUnit集成的IDE列表，以及关于JUnit扩展工具的详细内容。</FONT></SPAN> </P>
<P><SPAN class=italicbodycopy><EM><FONT size=2><B>Michel Casabianca</B> (</FONT></EM><A href="mailto:casa@sweetohm.net"><SPAN class=bodylink><FONT color=#000000 size=2><EM> casa@sweetohm.net</EM></FONT></SPAN></A><EM><FONT size=2>)</FONT></EM></SPAN><SPAN class=bodycopy><FONT size=2>是In-Fusio（一家为移动用户提供游戏服务的法国公司）的一名软件工程师，同时也是XML Pocket Reference（O'Reilly出版，2001年）一书的合著者。</FONT></SPAN> </P>
<P>
<TABLE cellPadding=5 width="100%" align=center bgColor=#f7f7e7 border=1 vspace="5" hspace="5">
<TBODY>
<TR>
<TD colSpan=2>
<CENTER><A name=t1><SPAN class=parahead1><STRONG>表1：编写测试实例中所使用的判定方法</STRONG></SPAN></A> </CENTER>
<P></P>
<TR bgColor=#fffff>
<TD><SPAN class=bodycopy><FONT size=2>assertEquals(期望原型,实际原型)</FONT></SPAN></TD>
<TD><SPAN class=bodycopy><FONT size=2>检查两个原型是否相等</FONT></SPAN></SPAN></TD></TR>
<TR>
<TD><SPAN class=bodycopy><FONT size=2>assertEquals(期望对象,实际对象)</FONT></SPAN></TD>
<TD><SPAN class=bodycopy><FONT size=2>利用对象的equals()方法检查两个对象是否相等</FONT></SPAN></TD></TR>
<TR bgColor=#fffff>
<TD><SPAN class=bodycopy><FONT size=2>assertSame(期望对象，实际对象)</FONT></SPAN></TD>
<TD><SPAN class=bodycopy><FONT size=2>检查具有相同内存地址的两个对象是否相等</FONT></SPAN></TD></TR>
<TR>
<TD><SPAN class=bodycopy><FONT size=2>assertNotSame(期望对象,实际对象)</FONT></SPAN></TD>
<TD><SPAN class=bodycopy><FONT size=2>检查具有不同内存地址的两个对象是否不相等</FONT></SPAN></TD></TR>
<TR bgColor=#fffff>
<TD><SPAN class=bodycopy><FONT size=2>assertNull(对象 对象)</FONT></SPAN></TD>
<TD><SPAN class=bodycopy><FONT size=2>检查一个对象是否为空</FONT></SPAN></TD></TR>
<TR>
<TD><SPAN class=bodycopy><FONT size=2>assertNotNull(对象 对象)</FONT></SPAN></TD>
<TD><SPAN class=bodycopy><FONT size=2>检查一个对象是否为非空</FONT></SPAN></TD></TR>
<TR bgColor=#fffff>
<TD><SPAN class=bodycopy><FONT size=2>assertTrue(布尔条件)</FONT></SPAN></TD>
<TD><SPAN class=bodycopy><FONT size=2>检查条件是否为真</FONT></SPAN></TD></TR>
<TR>
<TD><SPAN class=bodycopy><FONT size=2>assertFalse(布尔条件)</FONT></SPAN></TD>
<TD><SPAN class=bodycopy><FONT size=2>检查条件是否为假</FONT></SPAN></TD></TR></TBODY></TABLE></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD></TR><!-- Fake RS 2--></TBODY></TABLE></TD>
<TD width=10><IMG height=1 alt="" src="http://www.oracle.com/portalimages/pobtrans.gif" width=10 border=0></TD></TR>
<TR>
<TD width="100%" colSpan=3><IMG height=10 alt="" src="http://www.oracle.com/portalimages/pobtrans.gif" border=0></TD></TR></TBODY></TABLE></P></FONT></FONT><img src ="http://www.blogjava.net/relax/aggbug/550.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/relax/" target="_blank">轻松</a> 2005-01-21 16:35 <a href="http://www.blogjava.net/relax/archive/2005/01/21/550.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>