??xml version="1.0" encoding="utf-8" standalone="yes"?>国产成人麻豆亚洲综合无码精品 ,亚洲视频2020,亚洲av极品无码专区在线观看http://www.tkk7.com/relax/category/245.html记述我学习java的里E?/description>zh-cnWed, 07 Mar 2007 22:23:46 GMTWed, 07 Mar 2007 22:23:46 GMT60单元试利器 JUnit 4http://www.tkk7.com/relax/archive/2007/03/05/101945.htmlLLMon, 05 Mar 2007 08:15:00 GMThttp://www.tkk7.com/relax/archive/2007/03/05/101945.htmlhttp://www.tkk7.com/relax/comments/101945.htmlhttp://www.tkk7.com/relax/archive/2007/03/05/101945.html#Feedback0http://www.tkk7.com/relax/comments/commentRss/101945.htmlhttp://www.tkk7.com/relax/services/trackbacks/101945.html本文主要介绍了如何?JUnit 4 提供的各U功能开展有效的单元试Qƈ通过一个实例演CZ如何使用 Ant 执行自动化的单元试。本文假设读者对 Eclipse 下进?Java 开发有一定的l验Qƈ了解 Java 5 中的注解QannotationQ特性?/p>

引言

毋庸|疑Q程序员要对自己~写的代码负责,您不仅要保证它能通过~译Q正常地q行Q而且要满需求和设计预期的效果。单元测试正是验证代码行为是否满预期的有效手段之一。但不可否认Q做试是g很枯燥无的事情Q而一遍又一遍的试则更是让人生畏的工作。幸q的是,单元试工具 JUnit 使这一切变得简单艺术v来?/p>

JUnit ?Java C֌中知名度最高的单元试工具。它诞生?1997 q_?Erich Gamma ?Kent Beck 共同开发完成。其?Erich Gamma 是经典著作《设计模式:可复用面向对象Y件的基础》一书的作者之一Qƈ?Eclipse 中有很大的A献;Kent Beck 则是一位极限编E(XPQ方面的专家和先驱?/p>

麻雀虽小Q五脏俱全。JUnit 设计的非常小巧,但是功能却非常强大。Martin Fowler 如此评h JUnitQ在软g开发领域,从来没有如此少的代码vC如此重要的作用。它大大化了开发h员执行单元测试的隑ֺQ特别是 JUnit 4 使用 Java 5 中的注解QannotationQɋ试变得更加单?/p>



回页?/font>


JUnit 4 初体?/span>

在开始体?JUnit 4 之前Q我们需要以下Y件的支持Q?/p>

  • EclipseQ最为流行的 IDEQ它全面集成?JUnitQƈ从版?3.2 开始支?JUnit 4。当?JUnit q不依赖于Q?IDE。您可以?http://www.eclipse.org/ 上下载最新的 Eclipse 版本?
  • AntQ基?Java 的开源构建工P您可以在 http://ant.apache.org/ 上得到最新的版本和丰富的文档。Eclipse 中已l集成了 AntQ但是在撰写本文ӞEclipse 使用?Ant 版本较低Q必需 1.7 或者以上版本)Q不能很好的支持 JUnit 4?
  • JUnitQ它的官方网站是 http://www.junit.org/。您可以从上面获取关?JUnit 的最新消息。如果您和本文一样在 Eclipse 中?JUnitQ就不必再下载了?

首先为我们的体验新徏一?Java 工程 —?coolJUnit。现在需要做的是Q打开目 coolJUnit 的属性页 -> 选择“Java Build Path”子选项 -> 炚w“Add Library…”按?-> 在弹出的“Add Library”对话框中选择 JUnitQ?a >?Q,q在下一中选择版本 4.1 后点几ZFinish”按钮。这样便?JUnit 引入到当前项目库中了?/p>
? 为项目添?JUnit ?/b>
? 为项目添?JUnit ? src=
h?JDK 的版?/b>

JUnit 4.1 是基?Java 5 的升U版本,它用了 Tiger 中的很多新特性来化原有的使用方式。正因ؓ如此Q它q不能直接运行在 JDK1.4.x 版本上。如果您需要在 JDK1.4.x 版本使用 JUnit 的话Q请使用 3.8.1 版本?/p>

可以开始编写单元测试了吗?{等……,您打把单元试代码攑֜什么地方呢Q把它和被测试代码؜在一Pq显然会照成混ؕQ因为单元测试代码是不会出现在最l品中的。徏议您分别为单元测试代码与被测试代码创建单独的目录Qƈ保证试代码和被试代码使用相同的包名。这h保证了代码的分离Q同时还保证了查扄方便。遵照这条原则,我们在项?coolJUnit 根目录下d一个新目录 testsrcQƈ把它加入到项目源代码目录中(加入方式??Q?/p>
? 修改目源代码目?/b>
? 修改目源代码目? src=

现在我们得到了一?JUnit 的最佛_践:单元试代码和被试代码使用一L包,不同的目录?/p>

一切准备就l,一起开始体验如何?JUnit q行单元试吧。下面的例子来自W者的开发实践:工具c?WordDealUtil 中的静态方?wordFormat4DB 是专用于处理 Java 对象名称向数据库表名转换的方法(您可以在代码注释中可以得到更多详l的内容Q。下面是W一ơ编码完成后大致情ŞQ?/p>
package com.ai92.cooljunit;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 对名U、地址{字W串格式的内容进行格式检?
 * 或者格式化的工L
 * 
 * @author Ai92
 */
public class WordDealUtil {

	/**
	 * Java对象名称Q每个单词的头字母大写)按照
	 * 数据库命名的习惯q行格式?
	 * 格式化后的数据ؓ写字母Qƈ且用下划线分割命名单词
	 * 
	 * 例如QemployeeInfo l过格式化之后变?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();
	}
}

它是否能按照预期的效果执行呢Q尝试ؓ它编?JUnit 单元试代码如下Q?/p>
package com.ai92.cooljunit;

import static org.junit.Assert.assertEquals;
import org.junit.Test;

public class TestWordDealUtil {
	//试wordFormat4DB正常q行的情?
	@Test public void wordFormat4DBNormal(){
		String target = "employeeInfo";
		String result = WordDealUtil.wordFormat4DB(target);
		
		assertEquals("employee_info", result);
	}
}

很普通的一个类嘛!试c?TestWordDealUtil 之所以用“Test”开_完全是ؓ了更好的区分试cM被测试类。测试方?wordFormat4DBNormal 调用执行被测试方?WordDealUtil.wordFormat4DBQ以判断q行l果是否辑ֈ设计预期的效果。需要注意的是,试Ҏ wordFormat4DBNormal 需要按照一定的规范书写Q?/p>

  1. 试Ҏ必须使用注解 org.junit.Test 修饰?
  2. 试Ҏ必须使用 public void 修饰Q而且不能带有M参数?

试Ҏ中要处理的字W串为“employeeInfo”,按照设计目的Q处理后的结果应该ؓ“employee_info”。assertEquals 是由 JUnit 提供的一pd判断试l果是否正确的静态断aҎQ位于类 org.junit.Assert 中)之一Q我们用它执行结?result 和预期值“employee_info”进行比较,来判断测试是否成功?/p>

看看q行l果如何。在试cM点击右键Q在弹出菜单中选择 Run As JUnit Test。运行结果如下图所C:


? JUnit q行成功界面
? JUnit q行成功界面

l色的进度条提示我们Q测试运行通过了。但现在宣布代码通过了单元测试还为时q早。记住:您的单元试代码不是用来证明您是对的Q而是Z证明您没有错。因此单元测试的范围要全面,比如对边界倹{正常倹{错误值得试Q对代码可能出现的问题要全面预测Q而这也正是需求分析、详l设计环节中要考虑的。显Ӟ我们的测试才刚刚开始,l箋补充一些对Ҏ情况的测试:

public class TestWordDealUtil {
	…?
	//试 null 时的处理情况
	@Test public void wordFormat4DBNull(){
		String target = null;
		String result = WordDealUtil.wordFormat4DB(target);
		
		assertNull(result);
	}
	
	//试I字W串的处理情?
	@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);
	}
}

再次q行试。很遗憾QJUnit q行界面提示我们有两个测试情冉|通过试Q?a >?Q——当首字母大写时得到的处理结果与预期的有偏差Q造成试p|QfailureQ;而当试?null 的处理结果时Q则直接抛出了异常——测试错误(errorQ。显Ӟ被测试代码中q没有对首字母大写和 null q两U特D情况进行处理,修改如下Q?/p>
//修改后的ҎwordFormat4DB
/**
	 * Java对象名称Q每个单词的头字母大写)按照
	 * 数据库命名的习惯q行格式?
	 * 格式化后的数据ؓ写字母Qƈ且用下划线分割命名单词
	 * 如果参数name为nullQ则q回null
	 * 
	 * 例如QemployeeInfo l过格式化之后变?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();
	}


? JUnit q行p|界面
? JUnit q行p|界面

JUnit 测试失败的情况分ؓ两种Qfailure ?error。Failure 一般由单元试使用的断aҎ判断p|引vQ它表示在测试点发现了问题;?error 则是׃码异常引Pq是试目的之外的发玎ͼ它可能生于试代码本n的错误(试代码也是代码Q同h法保证完全没有缺PQ也可能是被试代码中的一个隐藏的bug?/p>
L讎ͼ

L记这一?JUnit 最佛_践:试M可能的错误。单元测试不是用来证明您是对的,而是Z证明您没有错?/p>

啊哈Q再ơ运行测试,l条又重现眼前。通过?WordDealUtil.wordFormat4DB 比较全面的单元测试,现在的代码已l比较稳定,可以作ؓ API 的一部分提供l其它模块用了?/p>

不知不觉中我们已l?JUnit 漂亮的完成了一ơ单元测试。可以体会到 JUnit 是多么轻量Q多么简单,Ҏ不需要花心思去研究Q这可以把更多的注意力攑֜更有意义的事情上——编写完整全面的单元试?/p>



回页?/font>


JUnit 深入

当然QJUnit 提供的功能决不仅仅如此简单,在接下来的内容中Q我们会看到 JUnit 中很多有用的Ҏ,掌握它们Ҏ灉|的编写单元测试代码非常有帮助?/p>

Fixture

何谓 FixtureQ它是指在执行一个或者多个测试方法时需要的一pd公共资源或者数据,例如试环境Q测试数据等{。在~写单元试的过E中Q您会发现在大部分的试Ҏ在进行真正的试之前都需要做大量的铺垫——ؓ设计准备 Fixture 而忙。这些铺垫过E占据的代码往往比真正测试的代码多得多,而且q个比率随着试的复杂程度的增加而递增。当多个试Ҏ都需要做同样的铺垫时Q重复代码的“坏味道”便在测试代码中弥O开来。这股“坏味道”会弄脏您的代码Q还会因为疏忽造成错误Q应该用一些手D|栚w它?/p>

JUnit 专门提供了设|公?Fixture 的方法,同一试cM的所有测试方法都可以q它来初始?Fixture 和注销 Fixture。和~写 JUnit 试Ҏ一P公共 Fixture 的设|也很简单,您只需要:

  1. 使用注解 org,junit.Before 修饰用于初始?Fixture 的方法?
  2. 使用注解 org.junit.After 修饰用于注销 Fixture 的方法?
  3. 保证q两U方法都使用 public void 修饰Q而且不能带有M参数?

遵@上面的三条原则,~写出的代码大体是这个样子:

//初始化FixtureҎ
@Before public void init(){……}

//注销FixtureҎ
@After public void destroy(){……}

q样Q在每一个测试方法执行之前,JUnit 会保?init Ҏ已经提前初始化测试环境,而当此测试方法执行完毕之后,JUnit 又会调用 destroy Ҏ注销试环境。注意是每一个测试方法的执行都会触发对公?Fixture 的设|,也就是说使用注解 Before 或?After 修饰的公?Fixture 讄Ҏ是方法别的Q?a >?Q。这样便可以保证各个独立的测试之间互不干扎ͼ以免其它试代码修改试环境或者测试数据媄响到其它试代码的准性?/p>
? ҎU别 Fixture 执行C意?/b>
? ҎU别 Fixture 执行C意? src=

可是Q这U?Fixture 讄方式q是引来了批评,因ؓ它效率低下,特别是在讄 Fixture 非常耗时的情况下Q例如设|数据库链接Q。而且对于不会发生变化的测试环境或者测试数据来_是不会媄响到试Ҏ的执行结果的Q也没有必要针Ҏ一个测试方法重新设|一?Fixture。因此在 JUnit 4 中引入了cȝ别的 Fixture 讄ҎQ编写规范如下:

  1. 使用注解 org,junit.BeforeClass 修饰用于初始?Fixture 的方法?
  2. 使用注解 org.junit.AfterClass 修饰用于注销 Fixture 的方法?
  3. 保证q两U方法都使用 public static void 修饰Q而且不能带有M参数?

cȝ别的 Fixture 仅会在测试类中所有测试方法执行之前执行初始化Qƈ在全部测试方法测试完毕之后执行注销ҎQ?a >?Q。代码范本如下:

//cȝ别Fixture初始化方?
@BeforeClass public static void dbInit(){……}
	
//cȝ别Fixture注销Ҏ
	@AfterClass public static void dbClose(){……}


? cȝ?Fixture 执行C意?/b>
? cȝ?Fixture 执行C意? src=

异常以及旉试

注解 org.junit.Test 中有两个非常有用的参敎ͼexpected ?timeout。参?expected 代表试Ҏ期望抛出指定的异常,如果q行试q没有抛个异常,?JUnit 会认个测试没有通过。这为验证被试Ҏ在错误的情况下是否会抛出预定的异常提供了便利。D例来_Ҏ supportDBChecker 用于查用户用的数据库版本是否在pȝ的支持的范围之内Q如果用户用了不被支持的数据库版本Q则会抛行时异常 UnsupportedDBVersionException。测试方?supportDBChecker 在数据库版本不支持时是否会抛出指定异常的单元试Ҏ大体如下Q?/p>
@Test(expected=UnsupportedDBVersionException.class)
	public void unsupportedDBCheck(){
		…?
}

注解 org.junit.Test 的另一个参?timeoutQ指定被试Ҏ被允许运行的最长时间应该是多少Q如果测试方法运行时间超q了指定的毫U数Q则JUnit认ؓ试p|。这个参数对于性能试有一定的帮助。例如,如果解析一份自定义?XML 文档p了多?1 U的旉Q就需要重新考虑 XML l构的设计,那单元测试方法可以这h写:

@Test(timeout=1000)
	public void selfXMLReader(){
		…?
}

忽略试Ҏ

JUnit 提供注解 org.junit.Ignore 用于暂时忽略某个试ҎQ因为有时候由于测试环境受限,q不能保证每一个测试方法都能正运行。例如下面的代码便表C由于没有了数据库链接,提示 JUnit 忽略试Ҏ unsupportedDBCheckQ?/p>
@ Ignore(“db is down?
@Test(expected=UnsupportedDBVersionException.class)
	public void unsupportedDBCheck(){
		…?
}

但是一定要心。注?org.junit.Ignore 只能用于暂时的忽略测试,如果需要永q忽略这些测试,一定要认被测试代码不再需要这些测试方法,以免忽略必要的测试点?/p>

试q行?/font>

又一个新概念出现了——测试运行器QJUnit 中所有的试Ҏ都是由它负责执行的。JUnit 为单元测试提供了默认的测试运行器Q但 JUnit q没有限制您必须使用默认的运行器。相反,您不仅可以定制自qq行器(所有的q行器都l承?org.junit.runner.RunnerQ,而且q可以ؓ每一个测试类指定使用某个具体的运行器。指定方法也很简单,使用注解 org.junit.runner.RunWith 在测试类上显式的声明要用的q行器即可:

@RunWith(CustomTestRunner.class)
public class TestWordDealUtil {
…?
}

显而易见,如果试cL有显式的声明使用哪一个测试运行器QJUnit 会启动默认的试q行器执行测试类Q比如上面提及的单元试代码Q。一般情况下Q默认测试运行器可以应对l大多数的单元测试要求;当?JUnit 提供的一些高U特性(例如卛_介绍的两个特性)或者针对特D需求定?JUnit 试方式Ӟ昑ּ的声明测试运行器必不可了?/p>

试套g

在实际项目中Q随着目q度的开展,单元试cM来多Q可是直到现在我们还只会一个一个的单独q行试c,q在实际目实践中肯定是不可行的。ؓ了解册个问题,JUnit 提供了一U批量运行测试类的方法,叫做试套g。这P每次需要验证系l功能正性时Q只执行一个或几个试套g便可以了。测试套件的写法非常单,您只需要遵循以下规则:

  1. 创徏一个空cM为测试套件的入口?
  2. 使用注解 org.junit.runner.RunWith ?org.junit.runners.Suite.SuiteClasses 修饰q个I类?
  3. ?org.junit.runners.Suite 作ؓ参数传入注解 RunWithQ以提示 JUnit 为此cM用套件运行器执行?
  4. 需要放入此试套g的测试类l成数组作ؓ注解 SuiteClasses 的参数?
  5. 保证q个I类使用 public 修饰Q而且存在公开的不带有M参数的构造函数?
package com.ai92.cooljunit;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
…?

/**
 * 扚w试 工具?中测试类
 * @author Ai92
 */
@RunWith(Suite.class)
@Suite.SuiteClasses({TestWordDealUtil.class})
public class RunAllUtilTestsSuite {
}

上例代码中,我们前文提到的试c?TestWordDealUtil 攑օ了测试套?RunAllUtilTestsSuite 中,?Eclipse 中运行测试套Ӟ可以看到试c?TestWordDealUtil 被调用执行了。测试套件中不仅可以包含基本的测试类Q而且可以包含其它的测试套Ӟq样可以很方便的分层理不同模块的单元测试代码。但是,您一定要保证试套g之间没有循环包含关系Q否则无的循环׃出现在您的面前……?/p>

参数化测?/font>

回顾一下我们在节?a >JUnit 初体?/font>”中丄实例。ؓ了保证单元测试的严}性,我们模拟了不同类型的字符串来试Ҏ的处理能力,为此我们~写大量的单元测试方法。可是这些测试方法都是大同小异:代码l构都是相同的,不同的仅仅是试数据和期望倹{有没有更好的方法将试Ҏ中相同的代码l构提取出来Q提高代码的重用度,减少复制_脓代码的烦|在以前的 JUnit 版本上,q没有好的解x法,而现在您可以使用 JUnit 提供的参数化试方式应对q个问题?/p>

参数化测试的~写E微有点ȝQ当然这是相对于 JUnit 中其它特性而言Q:

  1. 为准备用参数化试的测试类指定Ҏ的运行器 org.junit.runners.Parameterized?
  2. 为测试类声明几个变量Q分别用于存放期望值和试所用数据?
  3. 为测试类声明一个用注?org.junit.runners.Parameterized.Parameters 修饰的,q回gؓ java.util.Collection 的公共静态方法,q在此方法中初始化所有需要测试的参数寏V?
  4. 为测试类声明一个带有参数的公共构造函敎ͼq在其中为第二个环节中声明的几个变量赋倹{?
  5. ~写试ҎQ用定义的变量作ؓ参数q行试?

我们按照q个标准Q重新改造一番我们的单元试代码Q?/p>
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 时的处理情况
	          	{"", ""},								//试I字W串时的处理情况
	          	{"employee_info", "EmployeeInfo"},		//试当首字母大写时的情况
	          	{"employee_info_a", "employeeInfoA"},	//试当尾字母为大写时的情?
	          	{"employee_a_info", "employeeAInfo"}	//试多个相连字母大写时的情况
	    		});
		}
	
	 	/**
	 	* 参数化测试必ȝ构造函?
	 	* @param expected	期望的测试结果,对应参数集中的第一个参?
	 	* @param target	试数据Q对应参数集中的W二个参?
	 	*/
		public TestWordDealUtilWithParam(String expected , String target){
			this.expected = expected;
			this.target = target;
		}
	
	 	/**
	 	* 试?Java 对象名称到数据库名称的{?
	 	*/
		@Test public void wordFormat4DB(){
			assertEquals(expected, WordDealUtil.wordFormat4DB(target));
		}
}

很明显,代码瘦n了。在静态方?words 中,我们使用二维数组来构建测试所需要的参数列表Q其中每个数l中的元素的攄序q没有什么要求,只要和构造函C的顺序保持一致就可以了。现在如果再增加一U测试情况,只需要在静态方?words 中添加相应的数组卛_Q不再需要复制粘贴出一个新的方法出来了?/p>



回页?/font>


JUnit ?Ant

随着目的进展,目的规模在不断的膨胀Qؓ了保证项目的质量Q有计划的执行全面的单元试是非常有必要的。但单靠JUnit提供的测试套件很难胜任这工作,因ؓ目中单元测试类的个数在不停的增加,试套g却无法动态的识别新加入的单元试c,需要手动修Ҏ试套Ӟq是一个很Ҏ遗忘得步骤,E有疏忽׃影响全面单元试的覆盖率?/p>

当然解决的方法有多种多样Q其中将 JUnit 与构建利?Ant l合使用可以很简单的解决q个问题。Ant —?备受赞誉?Java 构徏工具。它凭借出色的易用性、^台无x以及对目自动试和自动部|的支持Q成Z多项目构E中不可或缺的独立工Pq已l成Z实上的标准。Ant 内置了对 JUnit 的支持,它提供了两个 TaskQjunit ?junitreportQ分别用于执?JUnit 单元试和生成测试结果报告。用这两个 Task ~写构徏脚本Q可以很单的完成每次全面单元试的Q务?

不过Q在使用 Ant q行 JUnit 之前Q您需要稍作一些配|。打开 Eclipse 首选项界面Q选择 Ant -> Runtime 首选项Q见?Q,?JUnit 4.1 ?JAR 文gd?Classpath Tab 中?Global Entries 讄w。记得检查一?Ant Home Entries 讄中?Ant 版本是否?1.7.0 之上Q如果不是请替换为最新版本的 Ant JAR 文g?/p>
? Ant Runtime 首选项
? Ant Runtime 首选项

剩下的工作就是要~写 Ant 构徏脚本 build.xml。虽然这个过E稍嫌繁琐,但这是一件一x逸的事情。现在我们就把前面编写的试用例都放|到 Ant 构徏脚本中执行,为项?coolJUnit 的构本添加一下内容:

<?xml version="1.0"?>
<!-- ============================================= 
     auto unittest task    
     ai92                                                                
     ========================================== -->
<project name="auto unittest task" default="junit and report" basedir=".">

		<property name="output folder" value="bin"/>

		<property name="src folder" value="src"/>
	
		<property name="test folder" value="testsrc"/>
	
		<property name="report folder" value="report" />

		<!-- - - - - - - - - - - - - - - - - - 
          target: test report folder init                      
         - - - - - - - - - - - - - - - - - -->
		<target name="test init">
			<mkdir dir="${report folder}"/>
		</target>
	
		<!-- - - - - - - - - - - - - - - - - - 
          target: compile                      
         - - - - - - - - - - - - - - - - - -->
		<target name="compile">
			<javac srcdir="${src folder}" destdir="${output folder}" />
			<echo>compilation complete!</echo>
		</target>

		<!-- - - - - - - - - - - - - - - - - - 
          target: compile test cases                      
         - - - - - - - - - - - - - - - - - -->
		<target name="test compile" depends="test init">
			<javac srcdir="${test folder}" destdir="${output folder}" />
			<echo>test compilation complete!</echo>
		</target>
	
		<target name="all compile" depends="compile, test compile">
		</target>
	
		<!-- ======================================== 
          target: auto test all test case and output report file                      
      	===================================== -->
		<target name="junit and report" depends="all compile">
			<junit printsummary="on" fork="true" showoutput="true">
				<classpath>
					<fileset dir="lib" includes="**/*.jar"/>
					<pathelement path="${output folder}"/>
				</classpath>
				<formatter type="xml" />
				<batchtest todir="${report folder}">
					<fileset dir="${output folder}">
						<include name="**/Test*.*" />
					</fileset>
				</batchtest>
			</junit>
			<junitreport todir="${report folder}">
				<fileset dir="${report folder}">
					<include name="TEST-*.xml" />
				</fileset>
				<report format="frames" todir="${report folder}" />
			</junitreport>
		</target>
</project>

Target junit report ?Ant 构徏脚本中的核心内容Q其?target 都是为它的执行提供前期服务。Task junit 会寻找输出目录下所有命名以“Test”开头的 class 文gQƈ执行它们。紧接着 Task junitreport 会将执行l果生成 HTML 格式的测试报告(?Q放|在“report folder”下?/p>

为整个项目的单元试cȝ定一U命名风根{不仅是Z区分cd的考虑Q这?Ant 扚w执行单元试也非常有帮助Q比如前面例子中的测试类都已“Test”打_而测试套件则以“Suite”结{?/p>
? junitreport 生成的测试报?/b>
? junitreport 生成的测试报? src=

现在执行一ơ全面的单元试变得非常单了Q只需要运行一?Ant 构徏脚本Q就可以走完所有流E,q能得到一份详的试报告。您可以?Ant 在线手册 中获得上面提及的每一?Ant 内置 task 的用细节?/p>



回页?/font>


ȝ

随着来多的开发h员开始认同ƈ接受极限~程QXPQ的思想Q单元测试的作用在Y件工E中变得来重要。本文旨在将最新的单元试工具 JUnit 4 介绍l您Q以及如何结?IDE Eclipse 和构建工?Ant 创徏自动化单元测试方案。ƈ且还期望您能够通过本文“感染”一些好的单元测试意识,因ؓ JUnit 本n仅仅是一份工兯已Q它的真正优势来自于它的思想和技术?/p>




回页?/font>


下蝲

描述 名字 大小 下蝲Ҏ
本文CZ代码 coolJUnit.zip 24 KB HTTP
关于下蝲Ҏ的信?/font>


参考资?

学习

获得产品和技?/b>



L 2007-03-05 16:15 发表评论
]]>
[软g试]HttpUnit-试用例(例子)[ZZ]http://www.tkk7.com/relax/archive/2005/01/31/867.htmlLLMon, 31 Jan 2005 07:06:00 GMThttp://www.tkk7.com/relax/archive/2005/01/31/867.htmlhttp://www.tkk7.com/relax/comments/867.htmlhttp://www.tkk7.com/relax/archive/2005/01/31/867.html#Feedback2http://www.tkk7.com/relax/comments/commentRss/867.htmlhttp://www.tkk7.com/relax/services/trackbacks/867.html(1)环境讄Q导入HttpUnit

(2)开始实践,写一个测试接口,起名为LoginTestInfQ?/STRONG>

/*
 * Created on 2004-12-17
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package org.apollo.test.util;

/**
 * @author SixSun
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */

/**
 *试用例~号 : 0001
 *试用例名称 : HttpUnit 登陆验证试用例
 *试目标 : 验证用户登陆是否成功
 *试q程 :
 *1、输入登陆地址的页面地址Q验证该面是否可被正常讉K?BR> *2、验证被讉K的页面是否是登陆面?BR> *3、输入非法用户名、密码,验证登陆p|?BR> *4、输入合法用户名、密码,验证登陆成功?BR> */     
public interface LoginTestInf {
    public void testValidPage() throws Exception;
    public void testIsLoginPage() throws Exception;
    public void testBadLogin() throws Exception;
    public void testGoodLogin() throws Exception;
}

(3)实现一个Junit TestCase 同时 implements LoginTestInf 接口Q?/STRONG>

/*
 * Created on 2004-12-17
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package org.apollo.test.util;

import java.net.URL;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.textui.TestRunner;
 
import com.meterware.httpunit.WebConversation;
import com.meterware.httpunit.WebRequest;
import com.meterware.httpunit.WebResponse;
import com.meterware.httpunit.WebForm;
import com.meterware.httpunit.GetMethodWebRequest;

import org.apollo.test.util.LoginTestInf;

/**
 * @author sixsun
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class LoginTest extends TestCase implements LoginTestInf {

 private String username = "suibian";
 private String password = "suibian";
 
    private WebConversation browser;
    private WebRequest requestIndex;
    private WebRequest requestLogin;
    private WebResponse responseIndex;
    private WebResponse responseLogin;
    private String urlSystem = "pȝ首页|址";
    private String urlLogin = "登陆界面|址";
 /*
  * @see TestCase#setUp()
  */
 protected void setUp() throws Exception {
        browser =  new WebConversation();
        requestIndex = new GetMethodWebRequest(urlSystem);
        responseIndex  = browser.getResponse(requestIndex);
        requestLogin = new GetMethodWebRequest(urlLogin);
        responseLogin  = browser.getResponse(requestLogin);       
 }
 
    //输入登陆地址的页面地址Q验证该面是否可被正常讉K
    public void testValidPage() throws Exception{
           assertNotNull("zsonline在网l上不存在!",responseIndex);
    }
   
    //验证被访问的面是否是登陆页?BR>    public void testIsLoginPage() throws Exception{
           URL currentUrl = responseLogin.getURL();
           String currentUrlStr = currentUrl.getProtocol() + "://" +currentUrl.getHost() + currentUrl.getPath();
           assertEquals("登陆面不是zsonline首页!" ,currentUrlStr,urlLogin);
    }
   
    //输入非法用户名、密码,验证登陆p|
    public void testBadLogin() throws Exception{
          WebForm form = responseLogin.getForms()[0];
          form.setParameter("username","badname");
          form.setParameter("password","badpassword");
          requestLogin = form.getRequest();
          responseLogin =  browser.getResponse(requestLogin);
          assertTrue("用户名不存在Q请认用户名输入是否完全正?区分大小?Q?,
                  responseLogin.getText().indexOf("用户名不存在Q请认用户名输入是否完全正?区分大小?Q?) != -1);
    }
   
   //输入合法用户名、密码,验证登陆成功
    public void testGoodLogin() throws Exception{
          WebForm form = responseLogin.getForms()[0];
          form.setParameter("username",username);
          form.setParameter("password",password);//此处需要填写真实密?BR>          requestLogin = form.getRequest();
          responseLogin =  browser.getResponse(requestLogin);
          assertTrue("转到'zsonline'【suibian】用户首失败!",responseLogin.getText().indexOf("用户试用户_zsonlineQ您好!") != -1);     
    }
 
    public static TestSuite suite(){
        return new TestSuite(LoginTest.class);
      }
      public static void main(String args[]){
        TestRunner.run(suite());
      }
}



L 2005-01-31 15:06 发表评论
]]>
在Eclipse中用JUnit(译)http://www.tkk7.com/relax/archive/2005/01/28/782.htmlLLFri, 28 Jan 2005 05:34:00 GMThttp://www.tkk7.com/relax/archive/2005/01/28/782.htmlhttp://www.tkk7.com/relax/comments/782.htmlhttp://www.tkk7.com/relax/archive/2005/01/28/782.html#Feedback1http://www.tkk7.com/relax/comments/commentRss/782.htmlhttp://www.tkk7.com/relax/services/trackbacks/782.html q篇文章向你介lJunitQ一个用来在目中进行测试和调试的工兗在介绍完TDDQ以试驱动开发)理论后,进一步讲解怎样在流行的Eclipse中徏立你自己的JUnit试。向你展C如何测试Hello Worldq样单的E序?BR>
    许多书上都讨Z自动试Q但是只有很的著作注意到这么一个问题,那就是怎样把这些测试组lv来。随着试的增加,攄和调用这些测试却变得更加ȝ。这成Z个重要问题,以至于出CTDDQ极限编E(XPQTDD得以普及。另外,你可以这L解TDDQ通过试来开发?BR>
    TDD的主要规?nbsp;:
    在编写程序代码之前,与之对应的自动测试必被写好。甚至程序代码ƈ不存在,那也要看见一个失败的试l果?BR>
    在测试通过后,副本代码必须被丢弃?BR>
    有一个具体步骤(可能指的是《Extreme Programming》)可以被Q何一个程序员来参考,而不需要特D的其他Ҏ。在我们开始写试之前Q这些步骤(章节Q应该被首先阅读——怎样l织自动试?BR>   
    讲解一下不同种cȝ试Q?BR>   
    单元试Q检模块(也就是类Q的正确性。如果对象需要访问外部的数据资源Q例如数据库Q就需要模拟一个mock objectsQ但在实际中真实数据与测试环境是不同的?BR>
    客户试Q这是功能性、系l、和验收试。用来测试整体的pȝҎ。在XP中,q些试qL写?BR>
    l合试Q介于用h试和单元试之间的桥梁。综合测试帮助测试应用程序的交互性。一般情况下Qmock objects不被用于l合试Q它会增加测试时间。同Pl合试l常依赖Ҏ的测试环境,例如数据库送来的测试数据。综合测试也需要用到外部类库。例如ؓJ2EE应用E序q行l合试的类库Cactus。解释这些测试超Z本文的范_需要更加详l的信息请参考http://jakarta.apache.org/cactus/?BR>
    开发h员测试:q是用来让开发h员检验自׃码或新函数的。对于每一个开发h员,只要有可能,需要有更多的测试来验代码。组l这些测试和l织E序代码一样重要?BR>
    在以下章节,只要提到“测试”,那就指的是开发h员测试?BR>
    我们几乎准备好开始徏立测试了Q先应该为我们的试选择名字。你也许会说Q“这不是问题Q把‘Test’这个字攑֜cd前面Q就好了Q”不会这么快Q让我来说一下这个步骤存在的问题Q?BR>
    在TDD中,被测试的cL者方法还不存在?BR>
    一个测试能够覆盖多个方法,甚至多个c,q是可能的?BR>
    以上只是一些普遍问题;q存在更多的问题?BR>
    让我来提一个徏议,在测试命名时Q测试类的名字应该让Z眼就知道q是一个测试类Q且能说明它要测试什么,注意是否和其他类重名。按照以上徏议做Q就很简单了Q也不用担心名字太长或难听?BR>
    卛_在Eclipse中用JUnit工具创徏我们W一个测试了。假设你已经下蝲了一个最新的Eclipse版本。如果还没有Q你应该d方站点http://www.eclipse.org下蝲。还需要JUnitQ也可以从http://www.junit.org/下蝲?BR>
    q行Eclipse。新Z个workplace目Q点L?>新徏->目Q选择Java目Q点M一步。v一个项目名Uͼ例如ProjectWithJUnit。点d成。这样就完成新项目的建立了。再来配|一下EclipseQ在构徏路径中添加JUnitcd。在工具条上点击目->属性,选择Java构徏路径Q库Q选择d外部JARQ浏览Junit被存储的目录Q选择junit.jarQ点L开。你会看见JUnit出现在库的列表中。点ȝ定,让Eclipse重徏路径?BR>
    现在开发我们的“Hello World”例子。按照TDD的规则,应该在代码徏立以前先把测试写好。ؓ了能够在某出开始,我们假设未来的类名是HelloWorldQƈ且有一个方法Say()Q这个方法返回String的|例如“Hello World!”)?BR>
    建立试Q在ProjectWithJUnit的标题上面点d键,选择新徏->其他Q展开“Java”选项Q选择JUnit。在双的栏目对话框中选择试案例Q然后下一步。参考图1?BR>
?. 在Eclipse中徏立JUnit试

    在测试类q一栏中Q写上将要被试的类名HelloWorld。选择一个测试案例的名字Q例如TestThatWeGetHelloWorldPromptQ是的,看上d长,但是很清楚它的行为。)点击完成?BR>
    TestThatWeGetHelloWorldPrompt的代码如下:

    import junit.framework.TestCase;

 

    public class TestThatWeGetHelloWorldPrompt

    extends TestCase {

        public TestThatWeGetHelloWorldPrompt(

            String name) {

            super(name);

        }

        public void testSay() {

            HelloWorld hi = new HelloWorld();

            assertEquals(\"Hello World!\", hi.say());

        }

        public static void main(String[] args) {

            junit.textui.TestRunner.run(

                TestThatWeGetHelloWorldPrompt.class);

        }

    }

 

    代码q不复杂Q只是有点与众不同。然而,让我们考察一下细节。我们承了JUnit的TestCasec,它在JUnit的javadocs定义为“运行众多测试的夹具。”JUnit也有TestSuitec,它是一l测试案例的集合Q但在本文中不做讨论?BR>
    建立试案例的步骤如下:
    

    1、徏立一个junit.framework.TestCase的实例?BR>
    2、定义一些以“test”开头的无返回方法(例如testWasTransactionSuccessful()QtestShow()Q等{)?BR>
   TestThatWeGetHelloWorldPrompt.java包含q些QTestCase的子cd一个叫做testSay()的方法。这个方法调用了assertEquals()函数Q它用来比较我们预期的值和由say()q回的倹{?BR>
    main()Ҏ用来q行试和显C出的。JUnit的TestRunner处理试Q提供基于图像和文本的输现Ş式。我们用基于文本的版本Q因为Eclipse支持它,且也适合我们。当开始运行后Q基于文本的版本试会以文本形式输出QEclipse会把q些输出自动变成囑փ界面的输出?BR>
    按照TDD规范Q首ơ运行测试,应该故意让它p|。点击运?>q行?>Junit试Q记住TestThatWeGetHelloWorldPrompt.java应该被突出的昄在包资源理器中Q。在左边H口Q应该看见JUnitH口而不是包资源理器,它显CZ个红条,一ơ失败的试Q具体的p|原因参看?。如果没有自动显C些内容,点击JUnit标签Q在底部的左边)?BR>

?. JUnit中失败的试

    

    很好Q的却失败了。现在我们来建立被测试代码:在包资源理器窗口的ProjectWithJUnit标题上右击,选择新徏->cR选择cdQ我们已l假设了它叫HelloWorldQ然后直接点d成。ؓHelloWorld.java填入下列代码Q?BR>
        public class HelloWorld {

            public String say() {

                return(\"Hello World!\");

            }

        }

    q段代码很简单,甚至不需要注解,我们再来看看l果。按照上面描q过的方式,在JUnit的窗口中昄了一个绿条,参看?。绿条证明测试成功?BR>

?. JUnit中成功的试

                              

    现在Q我们想再让试p|一ơ,但原因不同。这有助于展CJUnit试中不同的报错信息。修改assertEquals()代码Q把“Hello World!”变成“Hello Me!”。当再次q行JUnitӞl果变成了红条,在JUnitH口的底部输Zp|原因Q参看图4?BR>
?. JUnit中的ComparisonError

                              

    最后,我想说一下关于测试是开发过E中的必要部分的话题。测试代码一直是开发中的重要部分。经q近几年的发展,已得C很大的提高,q要归功于强大的理论研究Q比如“expectations-based development”等{)Q和快速发展的试工具包,q有试q程的改q。如果你对这文章感兴趣Q那请你׃些时间来正式的学习一下测试理论吧Q这对你的工作很有用?BR> 

    关于作者:
    Alexander Prohorenko 一名UNIXpȝ理员、网l安全管理员?BR>
    Olexiy Prohorenko    一名Java开发者居住在乌克兰的Dniepropetrovsk
  整理发布Q独孤求?/TD> 摘自QCSDN.net

L 2005-01-28 13:34 发表评论
]]>
junit使用明手?/title><link>http://www.tkk7.com/relax/archive/2005/01/28/780.html</link><dc:creator>L</dc:creator><author>L</author><pubDate>Fri, 28 Jan 2005 05:13:00 GMT</pubDate><guid>http://www.tkk7.com/relax/archive/2005/01/28/780.html</guid><wfw:comment>http://www.tkk7.com/relax/comments/780.html</wfw:comment><comments>http://www.tkk7.com/relax/archive/2005/01/28/780.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/relax/comments/commentRss/780.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/relax/services/trackbacks/780.html</trackback:ping><description><![CDATA[用XPq行开发的q程Qunit test是必不可的环节。作为unit testQjunit是首选的工具。本文从使用目的、如何用、以及用中需要考虑的问题,略描qCjunit的基本用法?BR><BR>使用目的<BR>      junit是java中书写unit test的frameworkQ目前一些流行的unit test工具大都都是在junit上扩展而来的。目前它的版本是junit3.8.1Q可以从www.junit.org上下载?BR><BR>用法<BR>1.       基本使用步骤QJunit的用非常简单,它的基本使用步骤Q?BR><BR>-          创徏Q从junit.framework.TestCasezunit test需要的test case<BR><BR>-          书写试ҎQ提供类g如下函数{֐的测试方法:<BR><BR>public void testXXXXX();<BR><BR>-          ~译Q书写完test case后,~译所写的test casec?BR><BR>-          q行Q启动junit test runnerQ来q行q个test case?BR><BR>Junit提供?个基本的test runnerQ字W界面和囑Ş界面。启动命令分别如下:<BR><BR>a 囑Ş界面Q?BR><BR>java junit.swingui.TestRunner XXXXX<BR><BR>b 字符界面Q?BR><BR>java junit.textui.TestRunner XXXXX<BR><BR>2.       使用例子Q?BR><BR>import junit.frmework.TestCase;<BR><BR>public class TestSample extends TestCaset{<BR><BR>       public void testMethod1(){<BR><BR>              assertTrue( true);<BR><BR>}<BR><BR>}<BR><BR>3.       setUp与tearDownQ这两个函数是junit framework中提供初始化和反初始化每个测试方法的。setUp在每个测试方法调用前被调用,负责初始化测试方法所需要的试环境QtearDown在每个测试方法被调用之后被调用,负责撤销试环境。它们与试Ҏ的关pd以描q如下:<BR><BR><BR><BR>    试开?-> setUp -> testXXXX -> tearDown ->试l束<BR><BR><BR><BR><BR>4.       使用例子Q?BR><BR>import junit.frmework.TestCase;<BR><BR>public class TestSample extends TestCaset{<BR><BR>       protected void setUp(){<BR><BR>              //初始化…?BR><BR>}<BR><BR><BR><BR>       public void testMethod1(){<BR><BR>              assertTrue( true);<BR><BR>}<BR><BR><BR><BR>potected void tearDown(){<BR><BR>      //撤销初始化…?BR><BR>}<BR><BR>}<BR><BR>5.       区分fail、exception?BR><BR>-          failQ期望出现的错误。生原因:assert函数出错Q如assertFalse(true)Q;fail函数产生Q如fail(…?Q?BR><BR>-          exceptionQ不期望出现的错误,属于unit testE序q行时抛出的异常。它和普通代码运行过E中抛出的runtime异常属于一U类型?BR><BR>对于assert、fail{函数请参见junit的javadoc?BR><BR>6.       使用例子Q?BR><BR>import junit.frmework.TestCase;<BR><BR>public class TestSample extends TestCaset{<BR><BR>       protected void setUp(){<BR><BR>              //初始化…?BR><BR>}<BR><BR><BR><BR>       public void testMethod1(){<BR><BR>              …?BR><BR>              try{<BR><BR>                     boolean b= …?BR><BR>                     assertTrue( b);<BR><BR>                     throw new Exception( “This is a test.?;<BR><BR>                     fail( “Unable point.?;     //不可能到?BR><BR>              }catch(Exception e){<BR><BR>                     fail( “Yes, I catch u?; //应该到达?BR><BR>}<BR><BR>…?BR><BR>}<BR><BR><BR><BR>potected void tearDown(){<BR><BR>      //撤销初始化…?BR><BR>}<BR><BR>}<BR><BR>7.       l装TestSuiteQ运行更多的test。在junit中,Test、TestCase和TestSuite三者组成了composiste pattern。通过l装自己的TestSuiteQ可以完成对d到这个TestSuite中的所有的TestCase的调用。而且q些定义的TestSuiteq可以组装成更大的TestSuiteQ这样同时也方便了对于不断增加的TestCase的管理和l护?BR><BR>      它的另一个好处就是,可以从这个TestCase树的L一个节点(TestSuite或TestCaseQ开始调用,来完成这个节点以下的所有TestCase的调用。提高了unit test的灵zL?BR><BR>8.       使用例子Q?BR><BR>import junit.framework.Test;<BR><BR>import junit.framework.TestSuite;<BR><BR>public class TestAll{<BR><BR>public class TestAll{<BR><BR>       //定义一个suiteQ对于junit的作用可以视为类gjava应用E序的main?BR><BR>   public static Test suite(){<BR><BR>       TestSuite suite = new TestSuite("Running all tests.");<BR><BR>       suite.addTestSuite( TestCase1.class);<BR><BR>       suite.addTestSuite( TestCase2.class);<BR><BR>       return suite;<BR><BR>   }<BR><BR>}<BR><BR>q行同运行单独的一个TestCase是一LQ参见step 1 “运行”?BR><BR>9.       使用Ant junit task。我们除了用java来直接运行junit之外Q我们还可以使用junit提供的junit task与antl合来运行。涉及的几个主要的ant task如下Q?BR><BR>-          <junit>Q定义一个junit task<BR><BR>-          <batchtest>Q位?lt;junit>中,q行多个TestCase<BR><BR>-          <test>Q位?lt;junit>中,q行单个TestCase<BR><BR>-          <formatter>Q位?lt;junit>中,定义一个测试结果输出格?BR><BR>-          <junitreport>Q定义一个junitreport task<BR><BR>-          <report>Q位?lt;junitreport>中,输出一个junit report<BR><BR>具体的语法请参见相关文档?BR><BR>10.   使用例子Q?BR><BR><junit printsummary="yes" haltonfailure="no"><BR><BR>   <classpath><BR><BR>       <path refid="classpath"/><BR><BR>       <pathelement location="${dist.junit}"/><BR><BR>   </classpath><BR><BR>   <BR><BR>   <formatter type="brief" usefile="false"/><BR><BR>   <formatter type="xml"/><BR><BR><BR><BR>   <batchtest todir="${doc.junitReport}"><BR><BR>       <fileset dir="${dist.junit}" includes="**/*Test.class" /><BR><BR>   </batchtest><BR><BR></junit><BR><BR><BR><BR><junitreport todir="${doc.junitReport}"><BR><BR>   <fileset dir="${doc.junitReport}"><BR><BR>       <include name="TEST*-*.xml"/><BR><BR>   </fileset><BR><BR>   <report format="frames" styledir="${junit.styleDir}" todir="${doc.junitReport}"/><BR><BR></junitreport><BR><BR>查表<BR>      junit的用ƈ不很难,然而要书写一个好的TestCase却ƈ非易事。一个不好的TestCase往往是既费了时_也v不了实际的作用。相反,一个好的TestCaseQ不仅可以很好的指出代码中存在的问题Q而且也可以作Z码更准确的文档,同时q在持箋集成的过E中起非帔R要的作用。在此给Z写TestCase旉要注意的几点Q?BR><BR>-          试的独立性:一ơ只试一个对象,方便定位出错的位|。这?层意思:一个TestCaseQ只试一个对象;一个TestMethodQ只试q个对象中的一个方法?BR><BR>-          l测试方法一个合适的名字?BR><BR>-          在assert函数中给出失败的原因Q如QassertTrue( “?should be true?  …?Q方便查错。在q个例子中,如果无法通过assertTrueQ那么给出的消息被昄。在junit中每个assert函数都有W一个参数是出错时显C消息的函数原型?BR><BR>-          试所有可能引起失败的地方Q如Q一个类中频J改动的函数。对于那些仅仅只含有getter/setter的类Q如果是由IDEQ如EclipseQ生的Q则可不;如果是h工写Q那么最好测试一下?BR><BR>-          在setUp和tearDown中的代码不应该是与测试方法相关的Q而应该是全局相关的。如针对与测试方法A和BQ在setUp和tearDown中的代码应该是A和B都需要的代码?BR><BR>-          试代码的组l:相同的包Q不同的目录。这P试代码可以讉K被测试类的protected变量/ҎQ方便测试代码的~写。放在不同的目录Q则方便了测试代码的理以及代码的打包和发布。一个例子如下:<BR><BR>src   <=源代码根目录<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>    <IMG style="VERTICAL-ALIGN: middle" height=19 src="http://www.uml.org.cn/j2ee/images/016.gif" width=19 border=0>-mod1<BR><BR>        <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   <=试代码根目?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>    <IMG style="VERTICAL-ALIGN: middle" height=19 src="http://www.uml.org.cn/j2ee/images/016.gif" width=19 border=0>-mod1<BR><BR>        <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.tkk7.com/relax/aggbug/780.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/relax/" target="_blank">L</a> 2005-01-28 13:13 <a href="http://www.tkk7.com/relax/archive/2005/01/28/780.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用HttpUnitq行集成试 http://www.tkk7.com/relax/archive/2005/01/27/743.htmlLLThu, 27 Jan 2005 01:56:00 GMThttp://www.tkk7.com/relax/archive/2005/01/27/743.htmlhttp://www.tkk7.com/relax/comments/743.htmlhttp://www.tkk7.com/relax/archive/2005/01/27/743.html#Feedback1http://www.tkk7.com/relax/comments/commentRss/743.htmlhttp://www.tkk7.com/relax/services/trackbacks/743.html内容摘要

HttpUnit是一个集成测试工P主要xWeb应用的测试,提供的帮助类让测试者可以通过Javacd服务器进行交互,q且服务器端的响应当作文本或者DOM对象q行处理。HttpUnitq提供了一个模拟Servlet容器Q让你可以不需要发布ServletQ就可以对Servlet的内部代码进行测试。本文中作者将详细的介l如何用HttpUnit提供的类完成集成试?/P>

1  HttpUnit?/B>

HttpUnit是SourceForge下面的一个开源项目,它是ZJUnit的一个测试框Ӟ主要x于测试Web应用Q解决用JUnit框架无法对远EWeb内容q行试的弊端。当前的最新版本是1.5.4。ؓ了让HtpUnit正常q行Q你应该安装JDK1.3.1或者以上版本?/P>

1.1  工作原理

HttpUnit通过模拟览器的行ؓQ处理页面框ӞframesQ?cookies,面跌{QredirectsQ等。通过HttpUnit提供的功能,你可以和服务器端q行信息交互Q将q回的网内容作为普通文本、XML Dom对象或者是作ؓ链接、页面框架、图像、表单、表格等的集合进行处理,然后使用JUnit框架q行试Q还可以导向一个新的页面,然后q行新页面的处理Q这个功能你可以处理一l在一个操作链中的面?/P>

1.2  和其他商业工LҎ

商业工具一般用记录、回攄功能来实现测试,但是q里有个~陷Q就是当面设计被修改以后,q些被记录的行ؓ׃能重用了Q需要重新录制才能l测试?/P>

举个例子Q如果页面上有个元素最先的设计是采用单选框Q这个时候你开始测试,那么q些工具记录的就是你的单w择动作Q但是如果你的设计发生了变化Q比如说我改成了下拉选择Q或者用文本框接受用户输入Q这时候,你以前录制的试q程无效了Q必要重新录制?/P>

而HttpUnit因ؓxҎq些控g的内容,所以不你的外在表现Ş式如何变化,都不影响你已定试的可重用性?/P>

更多的关于httpunit的信息请讉Khttpunit的主?A >http://httpunit.sourceforge.net

2  作者的演示环境

pȝq_QWindows 2000 Server

应用服务器:深圳金蝶的apusic3.0

开发工P eclipse 2.1.2

3  HttpUnit安装、环境配|?/B>

3.1  安装

1. 到HttpUnit的主http://httpunit.sourceforge.net下蝲最新的包文Ӟ当前的最新版本是1.5.4?

2. 下载的Zip包解压羃到c:/httpunit(后面?httpunit_home%引用该目?

3.2  环境配置

作者的演示E序都是在eclipse中开发、执行的Q所以环境配|都是以eclipseZQ如果你使用其他的开发工Ph据这些步骤进行环境配|?/P>

  1. 启动eclipseQ徏立一个java工程
  2. ?httpunit_home%/lib/*.jar; %httpunit_home%/jars/*.jar加入到该java工程的Java build Path变量?

4  如何使用httpunit处理面的内?/B>

WebConversationcLHttpUnit框架中最重要的类Q它用于模拟览器的行ؓ。其他几个重要的cLQ?/P>

WebRequestc,模仿客户hQ通过它可以向服务器发送信息?/P>

WebResponsec,模拟览器获取服务器端的响应信息?/P>

4.1  获取指定面的内?/B>

4.1.1  直接获取面内容

System.out.println("直接获取|页内容Q?);
//建立一个WebConversation实例
WebConversation wc = new WebConversation();

//向指定的URL发出hQ获取响?
WebResponse wr = wc.getResponse( "http://localhost:6888/HelloWorld.html" );

//用getTextҎ获取相应的全部内?
//用System.out.println获取的内容打印在控制台?
System.out.println( wr.getText() );

4.1.2  通过GetҎ讉K面q且加入参数

System.out.println("向服务器发送数据,然后获取|页内容Q?);
//建立一个WebConversation实例
WebConversation wc = new WebConversation();
//向指定的URL发出h
WebRequest req = new GetMethodWebRequest( "http://localhost:6888/HelloWorld.jsp" );
//l请求加上参? 
req.setParameter("username","姓名");
//获取响应对象
WebResponse resp = wc.getResponse( req );

//用getTextҎ获取相应的全部内?
//用System.out.println获取的内容打印在控制台?
System.out.println( resp.getText() );

4.1.3 通过PostҎ讉K面q且加入参数

System.out.println("使用Post方式向服务器发送数据,然后获取|页内容Q?);
//建立一个WebConversation实例
WebConversation wc = new WebConversation();
//向指定的URL发出h
WebRequest req = new PostMethodWebRequest( "http://localhost:6888/HelloWorld.jsp" );
//l请求加上参? 
req.setParameter("username","姓名");
//获取响应对象
WebResponse resp = wc.getResponse( req );

//用getTextҎ获取相应的全部内?
//用System.out.println获取的内容打印在控制台?
System.out.println( resp.getText() );

大家x一下上面代码中打了下划U的两处内容Q应该可以看刎ͼ使用Get、PostҎ讉K面的区别就是用的h对象不同?/P>

4.2  处理面中的链接

q里的演C是扑ֈ面中的某一个链接,然后模拟用户的单为,获得它指向文件的内容。比如在我的面HelloWorld.html中有一个链接,它显C的内容是TestLinkQ它指向我另一个页面TestLink.htm. TestLink.htm里面只显CTestLink.html几个字符?/P>

下面是处理代码:

System.out.println("获取面中链接指向页面的内容Q?);
//建立一个WebConversation实例
WebConversation wc = new WebConversation();
//获取响应对象
WebResponse   resp = wc.getResponse( "http://localhost:6888/HelloWorld.html" );
//获得面链接对象
WebLink       link = resp.getLinkWith( "TestLink" );
//模拟用户单击事g 
link.click();
//获得当前的响应对?
WebResponse   nextLink = wc.getCurrentPage();                                           
   
//用getTextҎ获取相应的全部内?
//用System.out.println获取的内容打印在控制台?
System.out.println( nextLink.getText() );

4.3  处理面中的表格

表格是用来控刉面显C的常规对象Q在HttpUnit中用数l来处理面中的多个表格Q你可以用resp.getTables()Ҏ获取面所有的表格对象。他们依照出现在面中的序保存在一个数l里面?/P>

[注意] Java中数l下标是?开始的Q所以取W一个表格应该是resp.getTables()[0]Q其他以此类推?/P>

下面的例子演C如何从面中取出第一个表格的内容q且他们@环显C出来:

System.out.println("获取面中表格的内容Q?);
//建立一个WebConversation实例
WebConversation wc = new WebConversation();
//获取响应对象
WebResponse   resp = wc.getResponse( "http://localhost:6888/HelloWorld.html" );
//获得对应的表格对?
WebTable webTable = resp.getTables()[0];
//表格对象的内容传递给字符串数l?
String[][] datas = webTable.asText();
//循环昄表格内容
int i = 0 ,j = 0;
int m = datas[0].length;
int n = datas.length;
while (i<n){
  j=0;
  while(j<m){
    System.out.println("表格中第"+(i+1)+"行第"+
 (j+1)+"列的内容是:"+datas[i][j]);
    ++j;
  }
  ++i;
}

4.4  处理面中的表单

表单是用来接受用戯入,也可以向用户昄用户已输入信息(如需要用户修Ҏ据时Q通常会显CZ以前输入q的信息Q,在HttpUnit中用数l来处理面中的多个表单Q你可以用resp.getForms()Ҏ获取面所有的表单对象。他们依照出现在面中的序保存在一个数l里面?/P>

[注意] Java中数l下标是?开始的Q所以取W一个表单应该是resp.getForms()[0]Q其他以此类推?/P>

下面的例子演C如何从面中取出第一个表单的内容q且他们@环显C出来:

System.out.println("获取面中表单的内容Q?);
//建立一个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<m){
   System.out.println("W?+(i+1)+"个控件的名字?+pNames[i]+
   "Q里面的内容?+webForm.getParameterValue(pNames[i]));
   ++i;
}

5  如何使用httpunitq行试

5.1  寚w面内容进行测?/B>

httpunit中的q部分测试完全采用了JUnit的测试方法,即直接将你期望的l果和页面中的输出内容进行比较。不q这里的试q单多了,只是字符串和字符串的比较?/P>

比如你期望中的页面显C是中有一个表|它是面中的W一个表|而且他的W一行第一列的数据应该是显CusernameQ那么你可以使用下面的代码进行自动化试Q?/P>

System.out.println("获取面中表格的内容q且q行试Q?);
//建立一个WebConversation实例
WebConversation wc = new WebConversation();
//获取响应对象
WebResponse   resp = wc.getResponse( "http://localhost:6888/TableTest.html" );
//获得对应的表格对?
WebTable webTable = resp.getTables()[0];
//表格对象的内容传递给字符串数l?
String[][] datas = webTable.asText();
//对表格内容进行测?
String expect = "中文";
Assert.assertEquals(expect,datas[0][0]);

5.2  对Servletq行试

除了寚w面内容进行测试外Q有时候(比如开发复杂的Servlets的时候)Q你需要对Servlet本n的代码块q行试Q这时候你可以选择HttpUnitQ它可以提供一个模拟的Servlet容器Q让你的Servlet代码不需要发布到Servlet容器Q如tomcatQ就可以直接试?

5.2.1  原理?/B>

使用httpunit试ServletӞ请创Z个ServletRunner的实例,他负责模拟Servlet容器环境。如果你只是试一个Servlet,你可以直接用registerServletҎ注册q个ServletQ如果需要配|多个ServletQ你可以~写自己的web.xmlQ然后在初始化ServletRunner的时候将它的位置作ؓ参数传给ServletRunner的构造器?/P>

在测试ServletӞ应该记得使用ServletUnitClientcM为客LQ他和前面用q的WebConversation差不多,都承自WebClientQ所以他们的调用方式基本一致。要注意的差别是Q在使用ServletUnitClientӞ他会忽略URL中的L地址信息Q而是直接指向他的ServletRunner实现的模拟环境?/P>

5.2.2  单测?/B>

本实例只是演C如何简单的讉KServletq且获取他的输出信息Q例子中的Servlet在接到用戯求的时候只是返回一串简单的字符ԌHello World!.

1. Servlet的代码如下:

public class MyServlet extends HttpServlet {

 public void service(HttpServletRequest req, HttpServletResponse resp)
 throws IOException
 {
  PrintWriter out = resp.getWriter();
  //向浏览器中写一个字W串Hello World!
  out.println("Hello World!");
  out.close();
 } 
          
}

2. 试的调用代码如下:

//创徏Servlet的运行环?
ServletRunner sr = new ServletRunner();
//向环境中注册Servlet
sr.registerServlet( "myServlet", MyServlet.class.getName() );
//创徏讉KServlet的客L
ServletUnitClient sc = sr.newClient();
//发送请?
WebRequest request   = new GetMethodWebRequest( "http://localhost/myServlet" );
//获得模拟服务器的信息
WebResponse response = sc.getResponse( request );
//获得的l果打印到控制台?
System.out.println(response.getText());

5.2.3  试Servlet的内部行?/B>

对于开发者来_仅仅试h和返回信息是不够的,所以HttpUnit提供的ServletRunner模拟器可以让你对被调用Servlet内部的行行测试。和单测试中不同Q这里用了InvocationContext获得该Servlet的环境,然后你可以通过InvocationContext对象针对request、response{对象或者是该Servlet的内部行为(非服务方法)q行操作?/P>

下面的代码演CZ如何使用HttpUnit模拟Servlet容器Qƈ且通过InvocationContext对象Q测试Servlet内部行ؓ的大部分工作Q比如控制request、session、response{?/P>

//创徏Servlet的运行环?
ServletRunner sr = new ServletRunner();
//向环境中注册Servlet
sr.registerServlet( "InternalServlet", InternalServlet.class.getName() );
//创徏讉KServlet的客L
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中获取的内容Q?+ic.getRequest().getParameter("pwd"));
     
//直接通过上下文获得response对象,q且向客L输出信息
ic.getResponse().getWriter().write("haha");
     
//直接通过上下文获得session对象Q控制session对象
//lsession赋?
ic.getRequest().getSession().setAttribute("username","timeson");
//获取session的?
System.out.println("session中的|"+ic.getRequest().getSession().getAttribute("username"));
          
//使用客户端获取返回信息,q且打印出来
WebResponse response = ic.getServletResponse();
System.out.println(response.getText());

[注意]

在测试Servlet的之前,你必通过InvocationContext完成Servlet中的serviceҎ中完成的工作Q因为通过newInvocationҎ获取InvocationContext实例的时候该Ҏq没有被调用?/P>

6  ȝ

本文中,作者详l的演示和介l了如何使用HttpUnit提供的类来进行集成测试,主要实现以下操作Q?/P>

  1. 模拟用户行ؓ向服务器发送请求,传递参?
  2. 模拟用户接受服务器的响应信息Qƈ且通过辅助cd析这些响应信息,l合JUnit框架q行试
  3. 使用HttpUnit提供的模拟Servler容器,试开发中的Servlet的内部行?

参考资?/B>

  1. HttpUnit帮助  http://httpunit.sourceforge.net
  2. JUnit帮助  http://junit.org/index.htm


L 2005-01-27 09:56 发表评论
]]>
如何使用Junit~写和组l测试程?/title><link>http://www.tkk7.com/relax/archive/2005/01/21/555.html</link><dc:creator>L</dc:creator><author>L</author><pubDate>Fri, 21 Jan 2005 09:30:00 GMT</pubDate><guid>http://www.tkk7.com/relax/archive/2005/01/21/555.html</guid><wfw:comment>http://www.tkk7.com/relax/comments/555.html</wfw:comment><comments>http://www.tkk7.com/relax/archive/2005/01/21/555.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/relax/comments/commentRss/555.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/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>以下是一份简单的教程Q向您展C如何用Junit~写和组l测试程序?BR></P> <P><B><FONT size=6>一个简单的试用例?/FONT></B></P> <P>您是怎样~写试代码的呢Q?/P> <P>在调试器中用表辑ּ也许是最单的办法。您可以不用重新~译Q就能改变调试器中的表达式,您甚臛_以在您看到运行的对象实例后再军_如何改变Q您也可以写一些作为测试的表达式将l果打印到标准输出。以上风格的试都有一些局限,因ؓ它们都需要h为的判断来分析程序运行的l果Q而且Q呈现给您的也是一些不友好的输出。您每次只能q行一个调试表辑ּQ如果一个程序有太多的输句将D您很难找到您需要的l果?/P> <P>JUnit Test不需要h的判断去解释Q而且一ơ可以运行很多的试。如果您需要测试某个东东的时候,您只要这么做卛_Q?/P> <OL> <LI>从TestCasel承Z个子cR? <LI>重写runTest()Ҏ? <LI>当您x查一个值时Q调用assertTrue()ҎQƈ传入一个布量真值来代表试通过?</LI></OL> <P>例如Qؓ了测试同一货币单位的两个钱数的和,我们包含了一个真实的DCZq两个钱数的和。如下:</P> <P>public void testSimpleAdd()</P> <P>{</P> <P>         Money m12CHF = new Money(12,"CHF");</P> <P>         Money m14CHF = new Money(14,"CHF");</P> <P>         Money expected= new Money(26,"CHF");</P> <P>         Money result = m12CHF.add(m14CHF)</P> <P>         assertTrue(expected.equals(result));</P> <P>}</P> <P>如果Q您要写的测试与以前写过的有些类|那就写一个模ѝ如果,您想q行多个试Q那徏立一个组?/P> <P> </P> <P><B><FONT size=6>模板</FONT></B>Q?/P> <P>当您有两个或多个试需要操作对象的同一或相q部分,该怎么办?</P> <P>试需要运行在部分内容已经定的对象上Q这些已知的部分被称作测试模ѝ当您在写测试的时候,您通常会发现您构徏试环境Q已知部分)的时间要比您真正比较试l果的时间要ѝ?/P> <P>从某U程度上_您如果仔l用构造函敎ͼ您写模板的时候也许更Ҏ些。不怎么P许多的保存内Ҏ自共享的模板。通常Q您能够这个模板应用到一些不同的试上。每个测试用例将传递相q的信息或参数给模板Q然后检查不同的l果?/P> <P>当您写一个通用的模板时Q下面是您所要做的:</P> <OL> <LI>从TestCase生成子类? <LI>l模板添加需要用的实例变量? <LI>覆写setUp()Ҏ来实例化变量? <LI>覆写tearDown()Ҏ来释放您在setUp()Ҏ中徏立的怹资源?</LI></OL> <P>例如Qؓ了写一些用到 12瑞士法郎Q?4瑞士法郎Q?8元不同l合的测试用例,那就首先写一个模板:</P> <P>public class MoneyTest extends TestCase</P> <P>{</P> <P>         private Money f12CHF;</P> <P>         private Money f14CHF;</P> <P>         private Money f28USD;</P> <P>         protected void setUp()</P> <P>         {</P> <P>                  f12CHF = new Money(12,"CHF");</P> <P>                  f14CHF = new Money(14,"CHF");</P> <P>                  f28USD = new Money(28,"USD");</P> <P>         }</P> <P>}</P> <P>一旦您写完了模板,那么Q您可以再写随意多的测试用例了?/P> <P><B><FONT size=6>试用例</FONT></B></P> <P>当您拥有了模板后Q您是怎样来写和调用单独的试用例呢?</P> <P>当没有模板的时候,写测试用例是单的--只需覆写TestCase的匿名子cM的runTestҎ。有模板后,生成TestCase的子cL写设|的代码。然后,为单独的试用例写匿名子cR然而,当写q一些测试以后,您将注意刎ͼ很多的代码行都浪费在语法上了?/P> <P>JUnit提供了一个简l的Ҏ来利用模板写试Q如下:</P> <P>1Q在包含模板的类中提供一个public void ҎQ通常U定Q方法名以test开头?/P> <P>例如Qؓ了测试Moeny 和MoneyBag的和Q如下:</P> <P>public void testMoneyMoneyBag()</P> <P>{</P> <P>         //[12 CHF] +[14 CHF] +[28 USD] == {[26 CHF] [28 USD] }</P> <P>         Money bag[] = {f26CHF,f28USD};</P> <P>         MoneyBag expected = new MoneyBag(bag);</P> <P>         assertEquals(expected,f12CHF.add(f28USD.add(f14CHF)));</P> <P>}</P> <P>创徏一个MoneyTest实例来运行这个用例的ҎQ如下:</P> <P>         new MoneyTest("testMoneyMoneyBag")</P> <P>当这个测试运行时Q这个参数名字被用来查找需要运行的Ҏ?/P> <P>当您有多个测试用例时Q可以将他们l织?套g)suite.</P> <P><FONT size=6><B>套gQsuiteQ?/B></FONT></P> <P>您怎样才能一ơ运行多个测试?</P> <P>只要您有了两个测试,您可能就希望一赯行他们。您当然可以每次只运行一个,但是很快您就会感到厌倦。JUnit提供了一个对象,TestSuiteQ以方便您一ơ完成Q意多的测试一赯行?/P> <P>例如Q只q行一个测试用例,您可能会执行Q?/P> <P>         TestResult result = (new MoneyTest("testMoneyMoneyBag")).run();</P> <P>q行两个试用例Q可以先产生一个套?Suite),然后这两个试用例包含其中Q如下:</P> <P>         TestSuite suite = new TestSuite();</P> <P>         suite.addTest(new MoneyTest("testMoneyMoneyBag"));</P> <P>         suite.addTest(new MoneyTest("testSimpleAdd"));</P> <P>         TestResult result = suite.run();</P> <P>您可以采取另外的一U方式来一ơ运行多个测试用例,那就是让JUnit自己从用例类(TestCase)中提取套?Suite)。您可以通过用例类QTestCaseQ的cd传递给套g(Suite)的构造函数来做到q点?FONT size=6></FONT></P> <P>         TestSuite suite = new TestSuite(MoneyTest.class);</P> <P>         TestResult result = suite.run();</P> <P>使用手工Ҏ的多数情冉|Q我们希望套件中只包含测试用例的一个子集。其他情况,推荐使用自动提取试套gҎQ它能够避免当您在新d了一个测试用例后Q还需要更改TestSuite(套g)产生代码?FONT size=6></FONT></P> <P>TestSuites(套g)不仅可以包含试用例Q它q可以包含实现Test接口的Q意对象。例如,您可以在您的代码中生一个套Ӟ同时Q我也生一个,然后我们可以产生一个包含上qC个套件的套g来一赯行?/P> <P>         TestSuite suite = new TestSuite();</P> <P>         suite.addTest(Kent.suite());</P> <P>         suite.addTest(Erich.suite());</P> <P>         TestResult result = suite.run();</P> <P><FONT size=6><B>TestRunner(试执行?</B></FONT></P> <P>您怎样q行试Qƈ攉执行后的l果Q?/P> <P>当您有了一个测试套件的时候,您就惌行它。Juint提供了工h定义q个套gq行q显C测试结果,您需使您的套件能被TestRunner(试q行?讉KQ您可以使用静态方法suite()Qƈ且返回一个suite(套g)来完成这工作?/P> <P>public static Test suite()</P> <P>{</P> <P>         TestSuite suite = new TestSuite();</P> <P>         suite.addTest(new MoneyTest("testMoneyEquals")); <BR>          suite.addTest(new MoneyTest("testSimpleAdd")); <BR>          return suite;<BR></P> <P>}</P> <P>或则Q采用自动提取的方式Q?/P> <P>public static Test suite() { <BR>          return new TestSuite(MoneyTest.class); <BR>}</P> <P>如果QTestCase没有定义suiteҎQ测试执行器自动尝试提取一个suite,q把以test开头的Ҏ装入套g?/P> <P>Juint提供了图形和文本两种方式的测试执行器Q启动方式分别是Qjava junit.awtui.TestRunner 或则 java junit.swingui.TestRunner.</P> <P>囑Ş界面的执行方式提供了一个窗口,内容包括Q?/P> <OL> <LI>一个输入文本框Q用来键入包含suiteҎ的类的名字? <LI>一个启动测试的按钮? <LI>一个进度条Q如果测试失败,他将从绿色变为红艌Ӏ? <LI>一个包含失败的试的列表?</LI></OL> <P>当测试不通过Ӟjuint在底部提供一个失败测试的报表。juint区分p|和错误。失败是预期的,q且使用断言assertions来做查的.错误是没有预计到的,象数l烦引越界。下囑֌含了一个失败的试?/P> <P align=center> </P> <P> </P> <P>当您改变代码后,您必重新启动图形界面窗口,q是J琐和耗时的。ؓ了避免这U情况,JUnit 的AWT 和Swing UIs 可以利用junit.runner.LoadingTestCollector Q这个工具在试的每ơ运行时都重新读入您的类。如果您惛_闭这个功能,L“reload classes”属性选项卛_。在帮助中您可以扑ֈ更详l的信息?/P> <P>有一个批处理来启动Junit.您可以在命o行中键入java junit.textui.TestRunner Q后跟包含suiteҎ的类名。这个方式得C些终端文本输出。另外一U启动的方式是在您的TestCasecMQ包含如下定义的mainҎ?/P> <P>例如Q启动MoneyTest的测试执行器Q?/P> <P>public static void main(String args[]) { <BR>          junit.textui.TestRunner.run(suite());<BR>}<BR></P> <P>当您定义了这个mainҎ后,您就可以在命令行中键入:java MoneyTest 来运行您的测试了?/P> <P>无论是图形方式还是文本方式,都要认在您的classpath上是否包含了junit.jar.<BR></P> <P> </P> <P>在本文中QTestCase--试用例QSuite--套gQTestRunner--试执行器,————译?/P><img src ="http://www.tkk7.com/relax/aggbug/555.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/relax/" target="_blank">L</a> 2005-01-21 17:30 <a href="http://www.tkk7.com/relax/archive/2005/01/21/555.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>循序渐进学习JUnit http://www.tkk7.com/relax/archive/2005/01/21/550.htmlLLFri, 21 Jan 2005 08:35:00 GMThttp://www.tkk7.com/relax/archive/2005/01/21/550.htmlhttp://www.tkk7.com/relax/comments/550.htmlhttp://www.tkk7.com/relax/archive/2005/01/21/550.html#Feedback0http://www.tkk7.com/relax/comments/commentRss/550.htmlhttp://www.tkk7.com/relax/services/trackbacks/550.html作者:Michel Casabianca

使用最行的开放资源测试框架之一学习单元试基础?/FONT>

使用JUnit可以大量减少Java代码中程序错误的个数QJUnit是一U流行的单元试框架Q用于在发布代码之前对其q行单元试。现在让我们来详l研I如何用诸如JUnit、Ant和Oracle9i JDeveloper{工h~写和运行单元测试?/FONT>

Z么用JUnitQ?/FONT>

多数开发h员都同意在发布代码之前应当对其进行测试,q利用工兯行回归(regressionQ测试。做q项工作的一个简单方法是在所有JavacM以main()Ҏ实施试。例如,假设使用ISO格式Q这意味着有一个以q一格式作ؓ参数的构造器和返回一个格式化的ISO字符串的toString()ҎQ以及一个GMT时区来编写一个Date的子cR?/FONT>清单1 是q个cȝ一个简单实现?/FONT>

不过Q这U测试方法ƈ不需要单元测试限定语QqualifierQ,原因如下Q?/FONT>

  • 在一个类中进行测试的最单元是ҎQ你应当Ҏ个方法进行单独测试,以准地扑և哪些Ҏ工作正常Q哪些方法工作不正常?/SPAN>
  • 即前面的测试失败,也应当对各个Ҏq行试。在此实施中Q如果单个测试失败,后面的测试将Ҏ不会q行。这意味着你不会知道不良代码在你的实施中所占的癑ֈ比?/SPAN>
  • 试代码会出现在生成的类中。这在类的大方面可能不是什么问题,但却可能会成为安全性因素之一Q例如,如果你的试嵌入了数据库q接密码Q那么这一信息很Ҏ用于已发布的cM?/SPAN>
  • 没有框架可以自动启动q一试Q你必须~写一个脚本来启动每一个测试?/SPAN>
  • 在编写一个报告时Q你必须~写自己的实玎ͼq定义规则,以方便地报告错误?/SPAN>

JUnit框架是设计用来解决q些问题的。这一框架主要是所有测试实例(UCؓ"TestCase"Q的一个父c,q提供工hq行所~写的测试、生成报告及定义试包(test suiteQ?/FONT>

让我们ؓIsoDatecȝ写一个测试:q个IsoDateTestcȝgQ?

import java.text.ParseException;
import junit.framework.TestCase;


/**
 * Test case for <code>IsoDate</code>.
 */
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());
  }
}

本例中要注意的重Ҏ已经~写了一个用于测试的独立c,因此可以对这些文件进行过滤,以避免将q一代码嵌入到将要发布的文档中。另外,本例qؓ你希望在你的代码中测试的每个Ҏ~写了一个专用测试方法,因此你将切地知道需要对哪些Ҏq行试、哪些方法工作正总及哪些方法工作不正常。如果在~写实施文档之前已经~写了该试Q你可以利用它来衡量工作的q展情况?/FONT>

安装q运行JUnit

要运行此CZ试实例Q必首先下载ƈ安装JUnit。JUnit的最新版本可以在JUnit的网?/FONT> www.junit.org免费下蝲。该软g包很(U?00KBQ,但其中包括了源代码和文档。要安装此程序,应首先对该Y件包q行解压~(junitxxx.zipQ。它创Z个目录(junitxxxQ,在此目录下有文档Q在doc目录中)、框架的应用~程接口QAPIQ文档(在javadoc目录中)、运行程序的库文Ӟjunit.jarQ以及示例测试实例(在junit目录中)。截x撰写本文ӞJUnit的最新版本ؓ3.8.1Q我是在此版本上对示例进行测试的?/FONT>

IsoDate Test

? q行IsoDate试

要运行此试实例Q将源文ӞIsoDate.java?/FONT>IsoDateTest.javaQ拷贝到Junit的安装目录下Q打开l端Q进入该目录Q然后输入以下命令行Q如果你正在使用UNIXQ:

export CLASSPATH=.:./junit.jar
javac *.java
或者,如果你正在WindowsQ输入以下命令行

set CLASSPATH=.;junit.jar
javac *.java

q些命o行对CLASSPATHq行讄Q其包含当前目录中的类和junit.jar库,q编译Java源文件?/FONT>

要在l端上运行该试Q输入以下命令行Q?/SPAN>

java junit.textui.TestRunner IsoDateTest

此命令行运行该试Qƈ?/FONT>?1所C的控制C昄试l果?/FONT>

才在此工具可以运行类名被传递到命o行中的单个测试。注意:只有对命令行的最后测试才在考虑之内Q以前的试都被忽略了。(看v来像一个程序错误,是吧Q)

JUnitq提供了利用AWTQ抽象窗口工具包Q或Swingq行试的图形界面。ؓ了利用此囑Ş界面q行试Q在l端上输入以下命令行Q?/SPAN>

java junit.awtui.TestRunner IsoDateTest

或者用Swing界面Q?

java junit.swingui.TestRunner IsoDateTest

此命令行显C?/FONT>?2所C的界面。要选择一个测试ƈ使其q行Q点d有三个点的按钮。这显CCLASSPATHQ还有测试包Q但我们在后面讨论Q中所有测试的列表。要q行试Q点?Run"按钮。测试应当正运行,q在?2所C的界面中显C结果?/FONT>

在此界面中你应当选中复选框"Reload Classes Every Run"Q以便运行器在运行测试类之前对它们进行重新加载。这样就可以方便地编辑、编译ƈq行试Q而不需要每ơ都启动囑Ş界面?/FONT>

在该复选框下面是一个进度条Q在q行较大的测试包Ӟ该进度条非常有用。运行的试、错误和p|的数量都会在q度条下面显C出来。再下面是一个失败列表和一个测试层ơ结构。失败消息显C在底部。通过点击Test HierarchyQ测试层ơ结构)面板Q然后再点击H口右上角的"Run"按钮Q即可运行单个测试方法。请CQ用命令行工具是不可能做到q些的?/SPAN>

注意Q当q行工具来启动测试类Ӟq些cdd在于CLASSPATH中。但是如果测试类存储在jar文g中,那么即ɘq些jar文g存在于CLASSPATH中,JUnit也不能找到这些测试类?/FONT>

Swing interface

? 用于q行试的Swing界面

qƈ不是一U启动测试的方便ҎQ但q运的是QJUnit已经被集成到了其他工P如Ant和Oracle9i JDeveloperQ中Q以帮助你开发测试ƈ使测试能够自动运行?/FONT>

~写Junit试实例

你已l看C试cȝ源代码对IsoDate实施q行了询问。现在让我们来研I这L试文g的实施?/FONT>

试实例由junit.frameword.TestCasel承而来是ؓ了利用JUnit框架的优炏V这个类的名字就是在被测试类的名字上附加"Test"。因Z正在试一个名为IsoDate的类Q所以其试cȝ名字是IsoDateTest。ؓ了访问除U有Ҏ之外的所有方法,q个c通常与被类在同一个包中?BR>

注意Q你必须Z希望试的在cM定义的每个方法都~写一个方法。你要测试构造器或用了ISO日期格式的方法,因此你将需要ؓ以ISO格式的字W串作ؓ参数的构造器和toString()Ҏ~写一个测试方法。其命名方式与测试类的命名方式类|在被试ҎQ或构造器Q前面附?test"?

试Ҏ的主体通过验证assertionQ断aQ对被测Ҏq行询问。例如,在toString()实施的测试方法中Q你希望认该方法已l对旉的设定进行了很好的说明(对于UNIXpȝ来说Q最初问世的旉?970q??日的午夜Q。要实施assertionQ你可以使用Junit框架提供的assertionҎ。这些方法在该框架的junit.framework.AssertcM被实施,q且可以在你的测试中被访问,q是因ؓAssert是TestCase的父cR这些方法可与Java中的关键字assertQ是在J2EE 1.4中新出现的)相比。一些assertionҎ可以查原始类型(如布型、整型等Q之间或对象之间是否相等Q利用equals()Ҏ查两个对象是否相{)。其他assertionҎ查两个对象是否相同、一个对象是否ؓ"I??非空"Q以及一个布|通常׃个表辑ּ生成Q是"?q是"?。在?1中对q些Ҏq行了ȝ?/FONT>

对于那些采用点cd或双_ֺcd参数的assertionQ存在一个第三种ҎQ即采用一个deltag为参数进行比较。另外还要注意,assertEquals()和assertSame()Ҏ一般不会生相同的l果。(两个h相同值的字符串可以不相同Q因为它们是两个h不同内存地址的不同对象。)因此QassertEquals()会验证assertion的有效性,而assertSame()则不会。注意,对于?1 中的每个assertionҎQ你q有一U选择Q就是引入另一个参敎ͼ如果assertionp|Q该参数׃l出一条解释性消息。例如,assertEqualsQint 期望? int 实际|可以与一个诸如assertEqualsQ字W串消息Qint期望|int实际|的消息一起用?

当一个assertionp|Ӟ该assertionҎ会抛Z个AssertFailedError或ComparisonFailure。AssertionFailedError由java.lang.Errorl承而来Q因此你不必在测试方法的throws语句中对其进行声明。而ComparisonFailure由AssertionFailedErrorl承而来Q因此你也不必对其进行声明。因为当一个assertionp|时会在测试方法中抛出一个错误,所以后面的assertion不会l运行。框架捕捉到q些错误q认定该试已经p|后,׃打印Z条说明错误的消息。这个消息由assertion生成Qƈ且被传递到assertionҎQ如果有的话Q?/FONT>

现在下面一行语句添加到testIsoDate()Ҏ的末:

assertEquals("This is a test",1,2);

现在~译q运行测试:

$ 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:<1> but was:<2>
      at IsoDateTest.testIsoDate
      (IsoDateTest.java:29)

FAILURES!!!
Tests run: 2,  Failures: 1,  Errors: 0

JUnit为每个已处理的测试打C个点Q显C字?F"来表C失败,q在assertionp|时显CZ条消息。此消息׃发送到assertionҎ的注释和assertion的结果组成(自动生成Q。从q里可以看出assertionҎ的参数顺序对于生成的消息非常重要。第一个参数是期望|而第二个参数则是实际倹{?/FONT>

如果在测试方法中出现了某U错误(例如Q抛Z一个异常)Q该工具׃其昄Z个错误(而不是由assertionp|而生的一?p|"Q。现在对IsoDateTestc进行修改,以将前面增加的一行语句用以下语句代替Q?/SPAN>

throw new Exception("This is a test"); 

然后~译q运行测试:

$ 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

该工具将该异常显CZؓ一个错误。因此,一个错误表CZ个错误的试ҎQ而不是表CZ个错误的试实施?/FONT>

Assertc还包括一个fail()ҎQ该版本带有解释性消息)Q该Ҏ通过抛出AssertionFailedError来中断正在运行的试。当你希望一个测试失败而不会调用一个判定方法时Qfail()Ҏ是非常有用的。例如,如果一D代码应当抛Z个异常而未抛出Q那么可以调用fail()Ҏ使该试p|Q方法如下:

public void testIndexOutOfBounds() {
  try {
       ArrayList list=new ArrayList();
       list.get(0);
       fail("IndexOutOfBoundsException   
           not thrown");
  } catch(IndexOutOfBoundsException e) {}
}

JUnit的高U特?/FONT>

在示例测试实例中Q你已经同时q行了所有的试。在现实中,你可能希望运行一个给定的试Ҏ来询问你正编写的实施ҎQ所以你需要定义一l要q行的测试。这是框架的junit.framework.TestSuitecȝ目的Q这个类其实只是一个容器,你可以向其中d一pd试。如果你正在q行toString()实施Qƈ希望q行相应的测试方法,那么你可以通过重写试的suite()Ҏ来通知q行器,Ҏ如下Q?/SPAN>

public static Test suite() {

  TestSuite suite= new TestSuite();
  suite.addTest(new IsoDateTest
("testToString"));
  return suite;
}

在此Ҏ中,你用具体CZ说明了一个TestSuite对象Qƈ向其中添加了试。ؓ了在ҎU定义测试,你可以利用构造器方法名作ؓ参数使测试类实例化。此构造器可按如下Ҏ实施Q?/FONT>

public IsoDateTest(String name) {
  super(name);
}

上面的构造器和方法添加到IsoDateTestc(q需要引入junit.framework.Test和junit.framework.TestSuiteQ,q在l端上输入:

selecting a test method

?Q选择一个测试方?

 

 
$ javac *.java
$ java junit.textui.TestRunner IsoDateTest
.
Time: 0,31
OK (1 test)

注意Q在d到测试包中的试Ҏ中,只运行了一个测试方法,即toString()Ҏ?/FONT>

你也可以利用囑Ş界面Q通过在图3所C的Test Hierarchy面板中选择试Ҏ来运行一个给定的试Ҏ。但是,要注意当整个试包被q行一ơ后Q该面板被填满?/FONT>

当你希望一个测试实例中的所有测试方法添加到一个TestSuite对象中时Q可以用一个专用构造器Q该构造器此试实例的类对象作ؓ参数。例如,你可以用IsoDateTestcd施suite()ҎQ方法如下:

public static Test suite() {
  return new TestSuite(IsoDateTest.class);
}

q有一些情况,你可能希望运行一l由其他试Q如在工E发布之前的所有测试)l成的测试。在q种情况下,你必ȝ写一个实施suite()Ҏ的类Q以建立希望q行的测试包。例如,假定你已l编写了试cAtest和Btest。ؓ了定义那些包含了cATest中的所有测试和在BTest中定义的试包的集合Q可以编写下面的c:

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;
  }
}

你完全可以像q行单个试实例那样q行q个试包。注意,如果一个测试在一个套件中d了两ơ,那么q行器将q行它两ơ(试包和q行器都不会查该试是否是唯一的)。ؓ了了解实际的试包的实施Q应当研IJunit本n的测试包。这些类的源代码存在于JUnit安装的junit/test目录下?/FONT>

test results

?Q显C测试结果的报告

一个main()ҎdC个测试或一个测试包中有时是非常方便的,因此可以在不使用q行器的情况下启动测试。例如,要将AllTests试包作Z个标准的JavaE序启动Q可以将下面的main()Ҏd到类中:

public static void main(String[] args) {
  junit.textui.TestRunner.run(suite());
}

现在可以通过输入java AllTests来启动这个测试包?

JUnit框架q提供了一U有效利用代码的ҎQ即资源集合到被称为fixture的对象集中。例如,该示例测试实例利用两个叫作epoch和eon的参考日期。将q些日期重新~译到每个方法测试中只是费旉Q而且q可能出现错误)。你可以用fixture重新~写试Q如清单2所C?/FONT>

你定义了两个参考日期,作ؓ试cȝD,q将它们~译C个setUp()Ҏ中。这一Ҏ在每个测试方法之前被调用。与其对应的Ҏ是tearDown()ҎQ它在每个试Ҏq行之后清除所有的资源Q在q个实施中,该方法事实上什么也没做Q因为垃圾收集器为我们完成了q项工作Q。现在编译这个测试实例(其源代码应当攑֜JUnit的安装目录中Qƈq行它:

$ javac *.java
$ java junit.textui.TestRunner IsoDateTest2
.setUp()
testIsoDate()
tearDown()
.setUp()
testToString()
tearDown()

Time: 0,373

OK (2 tests)

注意Q在该测试实例中建立了参考日期,因此在Q何测试方法中修改q些日期都不会对其他试产生不利影响。你可以代码放到这两个Ҏ中,以徏立和释放每个试所需要的资源Q如数据库连接)?/FONT>

JUnit发布版还提供了扩展模式(在包junit.extensions中)Q即test decor-atorsQ以提供像重复运行一个给定的试q样的新功能。它q提供了一个TestSuiteQ以方便你在独立的线E中同时q行所有测试,q在所有线E中的测试都完成时停止?/SPAN>

利用Ant使测试自动化

如前面所qͼ试q行器是非常原始的。如果你正在q行Ant来编译你的工E,那么~译文g是运行单元测试的好方法。(关于Ant的介l,请参考我的文章《Ant介》(Starting with AntQ,发表于Oracle杂志2002q?1Q?2月号中)?/FONT>

假设你的源文件在src目录中,所生成的类在tmp目录中,q且junit.jar库位于工E的libdirectory目录中,那么你可以编译Java源文Ӟq?/FONT>清单3中所C的~译文gQ在工程的根目录中)q行单元试?/FONT>

q个~译文g的核心是q行单元试的测试目标。运行这些测试是q个目标junit的唯一d。ؓ了运行这一可选Q务,必须首先junit.jar库放到Ant安装目录下的lib目录中,然后下蝲q安装同一目录中的Ant可选Q务库?/FONT>清单3中的CZ嵌套了一个classpathc,它包括JUnit库和工程的类Q示例中q嵌套了一个batchtest元素Q它利用一个选择适当源文件的fileset元素定义了将要运行的试。这个Q务还包括haltonfilure和haltonerror属性,它们告诉Ant在遇C个失败或错误时是否应当停止。如果将它们的D|ؓ"?Q那么Ant在遇到第一个失败或错误时将会停止,~译会p|Q显Ӟq表C在q行试q程中存在有问题Q。另一斚wQ如果将它们的D|ؓ"?Q其l果׃是非常明了Q即使测试失败,~译也会成功Q,但所有测试仍运行。printsummary属性指CAnt是否昄q行试的输出。数值withOutAndErr可以在开发测试时方便地告诉Ant昄标准输出和错误输出。数值off表示不显CZQ何内容,而on只显C测试报告(没有试cȝ输出Q。junitdh很多属性,详细内容请参考Ant的文档?/FONT>

Z试q一~译文gQ你需要徏立名字ؓsrc、tmp和lib的目录。将junit.jar库放到lib目录中,q将前面看到的示例Java源文件放到src目录中。打开l端Q进入该工程的根目录Qƈ输入antQ其l果为:

$ 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

Antq可以生成非常有用的HTML格式的测试报告。ؓ了生成这L报告Q将前面的测试目标用以下目标代替Q?/FONT>

<target name="test" depends="bin"
       description="Run JUnit tests">
  <junit haltonfailure="false"
        printsummary="withOutAndErr">
    <classpath refid="cp"/>
    <batchtest todir="${tmp}">
      <fileset dir="${src}" 
              includes="**/*Test*.java"/>
     </batchtest>

     <formatter type="xml"/>
  </junit>
  <junitreport todir="${tmp}">
    <fileset dir="${tmp}" 
            includes="TEST-*.xml"/>
    <report format="frames" 
           todir="${tmp}"/>
  </junitreport>
</target>

q一目标与前面的目标相同Q只是该目标在batchtext元素中增加了一个新的属?-todirQ它告诉Ant在tmp目录中生成可扩展的标记语aQXMLQ报告。该目标q增加了一个新的junitreport元素Q以便由XML文g生成一个HTML报告。这一元素要求在安装Ant的lib目录中安装Xalan库(详细内容见Ant文档的junitreport部分Q?/FONT>ant.apache.org/manual/install.htmlQ。这一元素q定义了使用todir属性生成的文g的目标目录。通过嵌套一个fileset元素来定义ؓ生成q一报告而需要处理的XML文g。期望的输出格式利用嵌套的报告元素来实现。该对象生成一个诸?/FONT>?所C的报告?

q类报告在单元试自动q行时特别有用(比如在夜间编译期_。在q些情况下,错误或失败不会中断测试,因此你必d前面提到的junitd的haltonfailure和haltonerror属性设|ؓ"?。这一报告对于衡量实施q程也非常有用(比如当你必须重写已有代码Ӟ或者在实施之前已经~写了测试的情况下)?/FONT>

Ant对启动JUnit囑Şq行器也非常有用。下面的对象会启动Swing试q行器:

<target name="testui" depends="bin"
        description="Run graphical JUnit">
<java classname="junit.swingui.TestRunner"
      classpathref="cp" 
      fork="true"/>
</target>

你应当在l端中运行这一对象Qƈ且在另一个终端或你喜Ƣ的IDE中用Ant对其q行~译。这U方式你不必在每次惌试代码旉启动囑Şq行器?

在Oracle9i Jdeveloper中的JUnit集成

Oracle9i JDeveloperq没有基于网l集成JUnitQ但是下载ƈ安装q一插g只需要几分钟的时间。ؓ了完成此q程Q选择JDeveloper?Help"菜单下的"Check for Updates"V这样将会打开IDE更新向导Q以q接到Oracle技术网站,下蝲该插件ƈ安装它。当安装该插件后Q需要关闭ƈ重启Oracle9i JDeveloper。注意,向导q会下蝲相关的文档?/FONT>

通过为每个Q务提供向|q个插g极大地提高了开发h员编写测试实例、测试包和fixture{的工作效率。要调用q些向导Q点?File"菜单下的"New",然后选择"General/Unit Tests"c,q从右侧的窗体中选择合适的向导。你也可以从界面上启动测试套件?/FONT>

当准备好寚w目进行代码测试后Q应当首先用专用向导来~写fixtureQ然后测试实例向导可以利用它们集成到试实例中。另外,q有一些用来生成自定义试fixture的向g及生成商务组件和数据库连接测试fixture的向对{这后两U向导生成专用代码,以用setUp()和tearDown()Ҏ讄和发布商务组件或数据库连接?/FONT>

当完成fixture后,下一步应当用合适的向导来生成测试实例,q些向导可以让你选择要测试的cdҎ。你q可以选择在这个测试中使用的fixture。这生成一个用测试方法的M完成的代码框架。最后应当生成套件来q行你的试。这个专用向D你选择要包括在套g中的试QƈZ生成整个cR要启动一个测试套Ӟ点击览器中的文Ӟq择Run。这会启动囑Ş界面q运行套件的试?/FONT>

?Help"菜单中选择"Help Topics"Q你会在JDeveloper文档中找到关于如何用这些向导的详细教程。这会打开帮助pȝH口。点?Unit Testing with JUnit",然后选择合适的教程?/FONT>

JUnit和JDeveloper之间的这U集成你能够只~写单元试中你感兴的那部分的代码Q而让工具Z~写重复的代码?/FONT>
下一?

讉KJUnit|站
www.junit.org

下蝲
Oracle9i Jdeveloper
otn.oracle.com/software/products/jdev/

Oracle9i应用服务?/SPAN>
otn.oracle.com/software/products/ias/

学习Oracle9i JDeveloper扩展
otn.oracle.com/products/jdev/htdocs/partners/addins

阅读Oracle9i JDeveloper文档
otn.oracle.com/docs/products/jdev/

JUnit最佛_?/STRONG>

下面是一些在使用JUnit时应当注意的技巧:

  • 在实施之前编写测试代码。这是一U合同驱动的实施方式?/SPAN>
  • 只测试那些可能会中断的方法(也就是说Q在多数情况下不应测试setter和getterҎQ。要可能地多进行测试,以避免回归测试。当试一个较大的应用E序Ӟ你可以在夜间~译时运行所有测试?
  • 一定要使用正确的JUnit扩展来测试特D的应用E序Q如使用Castus试J2EE应用E序Q?

值得p的时?/STRONG>

到现在,你应当已l清楚地知道使用JUnit框架和合适的工具实施单元试是多么快速而简单。关于单元测试的下一个目标是使你的CTO怿你在实施试时所必须p的时间是Z以后节省更多的时间。但是,当你考虑在检查老代码、修正错误和发布一个调试过的版本上所p的时_它可能花Ҏ个一天)Ӟ在开发过E的早期阶段捕获的代码错误毫无疑问是一很好的投资。这里ƈ没有考虑当错误代码不再位于块的顶部时开发h员必遵循的"black magic"步骤Q这些步骤包括:标记代码Q制作一个分支、修正代码错误、进行发布,以及代码修正合q到块中。所有这些步骤都非常耗时Qƈ且容易生错误?/FONT>

要开始用单元测试和JUnitQ请讉KJUnit|站Q?www.junit.org。你找到大量有用的文档Q包括用JUnit实施试的详l说明书Q、一个与JUnit集成的IDE列表Q以及关于JUnit扩展工具的详l内宏V?/FONT>

Michel Casabianca ( casa@sweetohm.net)是In-FusioQ一家ؓUd用户提供游戏服务的法国公司)的一名Y件工E师Q同时也是XML Pocket ReferenceQO'Reilly出版Q?001q_一书的合著者?/FONT>

?Q编写测试实例中所使用的判定方?/STRONG>

assertEquals(期望原型,实际原型) 查两个原型是否相{?/FONT>
assertEquals(期望对象,实际对象) 利用对象的equals()Ҏ查两个对象是否相{?/FONT>
assertSame(期望对象Q实际对? 查具有相同内存地址的两个对象是否相{?/FONT>
assertNotSame(期望对象,实际对象) 查具有不同内存地址的两个对象是否不相等
assertNull(对象 对象) 查一个对象是否ؓI?/FONT>
assertNotNull(对象 对象) 查一个对象是否ؓ非空
assertTrue(布尔条g) 查条件是否ؓ?/FONT>
assertFalse(布尔条g) 查条件是否ؓ?/FONT>



L 2005-01-21 16:35 发表评论
]]>
վ֩ģ壺 þþþþһ| ޸Ƶ| ۺ͵Ļ| պƵ| һѹۿ| ɬɬѹۿƷ| ߹ۿavÿո| һëƬ߲ŷƵ| һػƸѴƬ| ޹ֻ߹ۿ| ˬָ߳Ƶ| Ƭ߹ۿ| Ѹ弤Ƶ| С˵ͼۺ| Ʒ| Ʒ888| þù޾Ʒ鶹| Ƶ߹ۿ| ޹Ʒһ߹ۿ| þþþavӰ | պ˳| ҹþþӰԺ| ȫƴȫɫȫѴƬ| ۺϾƷһ| va߹ۿ| ߹ۿ| avһ| AV߹ۿ| ҵijdzӪѿ | ޾ƷѹۿƵ| Ʒһʽֱ | ѵƵ| һѹۿ| ޹Ʒѹۿ| ģ߹ۿ| | ձһ | ߹ۿƵ| ɫվwwwһ | ޾߹ۿ| ޺Ļ|