不久以前,developerWorks 的作者 Andrew Glover 撰寫(xiě)了一篇介紹 Groovy 的文章,該文章是 alt.lang.jre 系列的一部分,而 Groovy 是一個(gè)新提議的用于 Java 平臺(tái)的標(biāo)準(zhǔn)語(yǔ)言。讀者對(duì)這篇文章的反應(yīng)非常熱烈,所以我們決定開(kāi)辦這個(gè)專欄,提供使用這項(xiàng)熱門(mén)新技術(shù)的實(shí)用指導(dǎo)。本文是第一期,將介紹使用 Groovy 和 JUnit 對(duì) Java 代碼進(jìn)行單元測(cè)試的一個(gè)簡(jiǎn)單策略。

開(kāi)始之前,我首先要招認(rèn):我是一個(gè)單元測(cè)試狂。實(shí)際上,我總是無(wú)法編寫(xiě)足夠的單元測(cè)試。如果我相當(dāng)長(zhǎng)一段時(shí)間都在進(jìn)行開(kāi)發(fā),而 沒(méi)有編寫(xiě)相應(yīng)的單元測(cè)試,我就會(huì)覺(jué)得緊張。單元測(cè)試給我信心,讓我相信我的代碼能夠工作,而且我只要看一下,可以修改它,就不會(huì)害怕它會(huì)崩潰。

而且,作為一個(gè)單元測(cè)試狂,我喜歡編寫(xiě)多余的測(cè)試用例。但是,我的興奮不是來(lái)自 編寫(xiě)測(cè)試用例,而是 看著它們生效。所以,如果我能用更快的方式編寫(xiě)測(cè)試,我就能更迅速地看到它們的結(jié)果。這讓我感覺(jué)更好。更快一些!

后來(lái),我找到了 Groovy,它滿足了我的單元測(cè)試狂,而且至今為止,對(duì)我仍然有效。這種新語(yǔ)言給單元測(cè)試帶來(lái)的靈活性,非常令人興奮,值得認(rèn)真研究。本文是介紹 Groovy 實(shí)踐方面的新系列的第一部分,在文中,我將向您介紹使用 Groovy 進(jìn)行單元測(cè)試的快樂(lè)。我從概述開(kāi)始,概述 Groovy 對(duì) Java 平臺(tái)上的開(kāi)發(fā)所做的獨(dú)特貢獻(xiàn),然后轉(zhuǎn)而討論使用 Groovy 和 JUnit 進(jìn)行單元測(cè)試的細(xì)節(jié),其中重點(diǎn)放在 Groovy 對(duì) JUnit 的 TestCase 類(lèi)的擴(kuò)展上。最后,我用一個(gè)實(shí)用的示例進(jìn)行總結(jié),用第一手材料向您展示如何把 groovy 的這些特性與 Eclipse 和 Maven 集成在一起。

不要再堅(jiān)持 Java 純粹主義了!
在我開(kāi)始介紹用 Groovy 進(jìn)行單元測(cè)試的實(shí)際經(jīng)驗(yàn)之前,我認(rèn)為先談?wù)勔粋€(gè)更具一般性的問(wèn)題 —— 它在您的開(kāi)發(fā)工具箱中的位置,這非常重要。事實(shí)是,Groovy 不僅是運(yùn)行在 Java 運(yùn)行時(shí)環(huán)境(JRE)中的腳本語(yǔ)言,它還被提議作為用于 Java 平臺(tái)的標(biāo)準(zhǔn)語(yǔ)言。正如您們之中的人已經(jīng)從 alt.lang.jre 系列(請(qǐng)參閱 參考資料)中學(xué)習(xí)到的,在為 Java 平臺(tái)進(jìn)行腳本編程的時(shí)候,有無(wú)數(shù)的選擇,其中大多數(shù)是面向快速應(yīng)用程序開(kāi)發(fā)的高度靈活的環(huán)境。

雖然有這么豐富的選擇,但還是有許多開(kāi)發(fā)人選擇堅(jiān)持自己喜歡的、最熟悉的范式:Java 語(yǔ)言。雖然大多數(shù)情況下,Java 編程都是很好的選擇,但是它有一個(gè)非常重要的缺點(diǎn),蒙住了只看見(jiàn) Java 的好處的這些人的眼睛。正如一個(gè)智者曾經(jīng)指出的: 如果您僅有的一個(gè)工具是一把錘子,那么您看每個(gè)問(wèn)題時(shí)都會(huì)覺(jué)得它像是釘子。我認(rèn)為這句諺語(yǔ)道出了適用于軟件開(kāi)發(fā)的許多事實(shí)。

雖然我希望用這個(gè)系列說(shuō)服您 Java 不是也不應(yīng)當(dāng)是開(kāi)發(fā)應(yīng)用程序的惟一選擇,但該腳本確實(shí)既有適用的地方也有不適用的地方。專家和新手的區(qū)別在于:知道什么時(shí)候 運(yùn)用該腳本,什么時(shí)候 避免使用它。

關(guān)于本系列
把工具整合到開(kāi)發(fā)實(shí)踐中的關(guān)鍵是了解什么時(shí)候使用它,以及什么時(shí)候把它留在工具箱中。腳本語(yǔ)言能夠成為工具包中極為強(qiáng)大的附件,但是只有正確地應(yīng)用在適當(dāng)?shù)膱?chǎng)合時(shí)才是這樣。為了實(shí)現(xiàn) 實(shí)戰(zhàn) Groovy 系列文章這個(gè)目標(biāo),我專門(mén)研究了 Groovy 的一些實(shí)戰(zhàn),教給您什么時(shí)候怎樣才能成功地應(yīng)用它們。

例如,對(duì)于高性能、事務(wù)密集型、企業(yè)級(jí)應(yīng)用程序,Groovy 腳本通常不太適合;在這些情況下,您最好的選擇 可能是普通的 J2EE 系統(tǒng)。但另一方面,一些腳本 —— 特別是用 Groovy 編寫(xiě)的腳本 —— 會(huì)非常有用,因?yàn)樗苎杆俚貫樾⌒偷摹⒎浅L厥獾摹⒉皇切阅苊芗偷膽?yīng)用程序(例如配置系統(tǒng)/生成系統(tǒng))快速制作原型。對(duì)于報(bào)表應(yīng)用程序來(lái)說(shuō), Groovy 腳本也是近乎完美的選擇,而最重要的是,對(duì)單元測(cè)試更是如此。

為什么用 Groovy 進(jìn)行單元測(cè)試?
是什么讓 Groovy 比起其他腳本平臺(tái)顯得更具有吸引力呢?是它與Java 平臺(tái)無(wú)縫的集成。還是因?yàn)樗腔?Java 的語(yǔ)言(不像其他語(yǔ)言,是對(duì) JRE 的替代,因此可能基于舊版的處理器),對(duì)于 Java 開(kāi)發(fā)人員來(lái)說(shuō),Groovy 意味著一條短得讓人難以置信的學(xué)習(xí)曲線。而且一旦將這條學(xué)習(xí)曲線拉直,Groovy 就能提供一個(gè)無(wú)與倫比的快速開(kāi)發(fā)平臺(tái)。

從這個(gè)角度來(lái)說(shuō),Groovy 成功的秘密,在于它的語(yǔ)法 就是 Java 語(yǔ)法,但是規(guī)則更少。例如,Groovy 不要求使用分號(hào),變量類(lèi)型和訪問(wèn)修飾符也是可選的。而且,Groovy 利用了標(biāo)準(zhǔn)的 Java 庫(kù),這些都是您已經(jīng)很熟悉的,包括 CollectionsFile/IO。而且,您還可以利用任何 Groovy 提供的 Java 庫(kù),包括 JUnit。

事實(shí)上,令人放松的類(lèi) Java 語(yǔ)法、對(duì)標(biāo)準(zhǔn) Java 庫(kù)的重用以及快捷的生成-運(yùn)行周期,這些都使 Groovy 成為快速開(kāi)發(fā)單元測(cè)試的理想替代品。但是會(huì)說(shuō)的不如會(huì)做的,還是讓我們?cè)诖a中看看它的實(shí)際效果!

JUnit 和 Groovy
用 Groovy 對(duì) Java 代碼進(jìn)行單元測(cè)試簡(jiǎn)單得不能再簡(jiǎn)單了,有很多入門(mén)選擇。最直接的選擇是沿襲行業(yè)標(biāo)準(zhǔn) —— JUnit。Unit 的簡(jiǎn)單性和其功能的強(qiáng)大都是無(wú)與倫比的,作為非常有幫助的 Java 開(kāi)發(fā)工具,它的普遍性也是無(wú)與倫比的,而且沒(méi)有什么能夠阻擋 JUnit 和 Groovy 結(jié)合,所以為什么多此一舉呢?實(shí)際上,只要您看過(guò) JUnit 和 Groovy 在一起工作,我敢打賭您就永遠(yuǎn)再也不會(huì)回頭!在這里,要記住的關(guān)鍵的事,您在 Java 語(yǔ)言中能用 JUnit 做到的事,在 Groovy 中用 JUnit 也全都能做到;但是需要的代碼要少得多。

入門(mén)
在您下載了 JUnit 和 Groovy(請(qǐng)參閱 參考資料)之后,您將有兩個(gè)選擇。第一個(gè)選擇是編寫(xiě)普通的 JUnit 測(cè)試用例,就像以前一直做的那樣,擴(kuò)展 JUnit 令人稱贊的 TestCase。第二個(gè)選擇是應(yīng)用 Groovy 簡(jiǎn)潔的 GroovyTestCase 擴(kuò)展,它會(huì)擴(kuò)展 JUnit 的 TestCase。第一個(gè)選項(xiàng)是您最快的成功途徑,它擁有最多 與 Java 類(lèi)似的相似性。而另一方面,第二個(gè)選擇則把您推進(jìn)了 Groovey 的世界,這個(gè)選擇有最大的敏捷性。

開(kāi)始的時(shí)候,我們來(lái)想像一個(gè) Java 對(duì)象,該對(duì)象對(duì)指定的 string 應(yīng)用了一個(gè)過(guò)濾器,并根據(jù)匹配結(jié)果返回 boolean 值。該過(guò)濾器可以是簡(jiǎn)單的 string 操作,例如 indexOf(),也可以更強(qiáng)大一些,是正則表達(dá)式。可能要通過(guò) setFilter() 方法在運(yùn)行時(shí)設(shè)置將使用的過(guò)濾器, apply() 方法接受要過(guò)濾的 string。清單 1 用普通的 Java 代碼顯示了這個(gè)示例的 Filter 接口:

清單 1. 一個(gè)簡(jiǎn)單的 Java Filter 接口

public interface Filter {
  void setFilter(String fltr);  
  boolean applyFilter(String value);
}

我們的想法是用這個(gè)特性從大的列表中過(guò)濾掉想要的或者不想要的包名。所以,我建立了兩個(gè)實(shí)現(xiàn): RegexPackageFilterSimplePackageFilter

把 Groovy 和 JUnit 的強(qiáng)大功能與簡(jiǎn)單性結(jié)合在一起,就形成了如清單 2 所示的簡(jiǎn)潔的測(cè)試套件:

清單 2. 用 JUunit 制作的 Groovy RegexFilterTest

import junit.framework.TestCase
import com.vanward.sedona.frmwrk.filter.impl.RegexPackageFilter

class RegexFilterTest extends TestCase {  

  void testSimpleRegex() {
    fltr = new RegexPackageFilter()
    fltr.setFilter("java.*")
    val = fltr.applyFilter("java.lang.String")		
    assertEquals("value should be true", true, val)		
  }
}

不管您是否熟悉 Groovy,清單 2 中的代碼對(duì)您來(lái)說(shuō)應(yīng)當(dāng)很面熟,因?yàn)樗徊贿^(guò)是沒(méi)有分號(hào)、訪問(wèn)修飾符或變量類(lèi)型的 Java 代碼而已!上面的 JUnit 測(cè)試中有一個(gè)測(cè)試用例 testSimpleRegex(),它試圖斷言 RegexPackageFilter 用正則表達(dá)式 "java.*" 正確地找到了與 “ java.lang.String”匹配的對(duì)象。

Groovy 擴(kuò)展了 JUnit
擴(kuò)展 JUnit 的 TestCase 類(lèi),加入附加特性,實(shí)際上是每個(gè) JUnit 擴(kuò)展通常采用的技術(shù)。例如,DbUnit 框架(請(qǐng)參閱 參考資料)提供了一個(gè)方便的 DatabaseTestCase 類(lèi),能夠比以往任何時(shí)候都更容易地管理數(shù)據(jù)庫(kù)的狀態(tài),還有重要的 MockStrutsTestCase(來(lái)自 StrutsTestCase 框架;請(qǐng)參閱 參考資料),它生成虛擬的 servlet 容器,用來(lái)執(zhí)行 struts 代碼。這兩個(gè)強(qiáng)大的框架都極好地?cái)U(kuò)展了 JUnit,提供了 JUnit 核心代碼中所沒(méi)有的其他特性;而現(xiàn)在 Groovy 來(lái)了,它也是這么做的!

與 StrutsTestCase 和 DbUnit 一樣,Groovy 對(duì) JUnit 的 TestCase 的擴(kuò)展給您的工具箱帶來(lái)了一些重要的新特性。這個(gè)特殊的擴(kuò)展允許您通過(guò) groovy 命令運(yùn)行測(cè)試套件,而且提供了一套新的 assert 方法。可以用這些方法很方便地?cái)嘌阅_本的運(yùn)行是否正確,以及斷言各種數(shù)組類(lèi)型的長(zhǎng)度和內(nèi)容等。

享受 GroovyTestCase 的快樂(lè)
了解 GroovyTestCase 的能力最好的辦法,莫過(guò)于實(shí)際看到它的效果。在清單 3 中,我已經(jīng)編寫(xiě)了一個(gè)新的 SimpleFilterTest,但是這次我要擴(kuò)展 GroovyTestCase 來(lái)實(shí)現(xiàn)它:

清單 3. 一個(gè)真正的 GroovyTestCase

import groovy.util.GroovyTestCase
import com.vanward.sedona.frmwrk.filter.impl.SimplePackageFilter

class SimpleFilterTest extends GroovyTestCase {
	
  void testSimpleJavaPackage() {
    fltr = new SimplePackageFilter()
    fltr.setFilter("java.")		
    val = fltr.applyFilter("java.lang.String")		
    assertEquals("value should be true", true, val)
  }	
}

請(qǐng)注意,可以通過(guò)命令行來(lái)運(yùn)行該測(cè)試套件,沒(méi)有運(yùn)行基于 Java 的 JUnit 測(cè)試套件所需要的 main() 方法。實(shí)際上,如果我用 Java 代碼編寫(xiě)上面的 SimpleFilterTest,那么代碼看起來(lái)會(huì)像清單 4 所示的那樣:

清單 4. 用 Java 代碼編寫(xiě)的同樣的測(cè)試用例

import junit.framework.TestCase;
import com.vanward.sedona.frmwrk.filter.Filter;
import com.vanward.sedona.frmwrk.filter.impl.SimplePackageFilter;

public class SimplePackageFilterTest extends TestCase {       

   public void testSimpleRegex() {
	Filter fltr = new SimplePackageFilter();
	fltr.setFilter("java.");
	boolean val = fltr.applyFilter("java.lang.String");
	assertEquals("value should be true", true, val);
   }
	
   public static void main(String[] args) {
 	junit.textui.TestRunner.run(SimplePackageFilterTest.class);
   }
}

用斷言進(jìn)行測(cè)試
除了可以讓您通過(guò)命令行運(yùn)行測(cè)試之外, GroovyTestCase 還向您提供了一些特別方便的 assert 方法。例如, assertArrayEquals,它可以檢查兩個(gè)數(shù)據(jù)中對(duì)應(yīng)的每一個(gè)值和各自的長(zhǎng)度,從而斷言這兩個(gè)數(shù)據(jù)是否相等。從清單 5 的示例開(kāi)始,就可以看到 Groovy 斷言的實(shí)際效果,清單 5 是一個(gè)簡(jiǎn)潔的基于 Java 的方法,它把 string 分解成數(shù)組。(請(qǐng)注意,我可能使用了 Java 1.4 中新添加的 string 特性編寫(xiě)以下的示例類(lèi)。我采用 Jakarta Commons StringUtils 類(lèi)來(lái)確保與 Java 1.3 的后向兼容性。)

清單 5. 定義一個(gè) Java StringSplitter 類(lèi)

import org.apache.commons.lang.StringUtils;

public class StringSplitter {
  public static String[] split(final String input, final String separator){
   return StringUtils.split(input, separator);
  }
}

清單 6 展示了用 Groovy 測(cè)試套件及其對(duì)應(yīng)的 assertArrayEquals 方法對(duì)這個(gè)類(lèi)進(jìn)行測(cè)試是多么簡(jiǎn)單:

清單 6. 使用 GroovyTestCase 的 assertArrayEquals 方法

import groovy.util.GroovyTestCase
import com.vanward.resource.string.StringSplitter

class StringSplitTest extends GroovyTestCase {
	
  void testFullSplit() {
    splitAr = StringSplitter.split("groovy.util.GroovyTestCase", ".")		
    expect = ["groovy", "util", "GroovyTestCase"].toArray()
    assertArrayEquals(expect, splitAr)		
  }	
}

其他方法
Groovy 可以讓您單獨(dú)或成批運(yùn)行測(cè)試。使用 GroovyTestCase 擴(kuò)展,運(yùn)行單個(gè)測(cè)試毫不費(fèi)力。只要運(yùn)行 groovy 命令,后面跟著要運(yùn)行的測(cè)試套件即可,如清單 7 所示:

清單 7. 通過(guò) groovy 命令運(yùn)行 GroovyTestCase 測(cè)試用例

$./groovy test/com/vanward/sedona/frmwrk/filter/impl/SimpleFilterTest.groovy
.
Time: 0.047

OK (1 test)

Groovy 還提供了一個(gè)標(biāo)準(zhǔn)的 JUnit 測(cè)試套件,叫作 GroovyTestSuite。只要運(yùn)行該測(cè)試套件,把腳本的路徑傳給它,它就會(huì)運(yùn)行腳本,就像 groovy 命令一樣。這項(xiàng)技術(shù)的好處是,它可以讓您在 IDE 中運(yùn)行腳本。例如,在 Eclipse 中,我只是為示例項(xiàng)目建立了一個(gè)新的運(yùn)行配置(一定要選中 “Include external jars when searching for a main class”),然后找到主類(lèi) groovy.util.GroovyTestSuite,如圖 1 所示:

圖 1. 用 Eclipse 運(yùn)行 GroovyTestSuite
圖 1. 用 Eclipse 運(yùn)行 GroovyTestSuite

在圖 2 中,您可以看到當(dāng)點(diǎn)擊 Arguments 標(biāo)簽,寫(xiě)入腳本的路徑時(shí),會(huì)發(fā)生了什么:

圖 2. 在 Eclipse 中指定腳本的路徑
圖 2. 在 Eclipse 中指定腳本的路徑

運(yùn)行一個(gè)自己喜歡的 JUnit Groovy 腳本,實(shí)在是很簡(jiǎn)單,只要在 Eclipse 中找到對(duì)應(yīng)的運(yùn)行配置就可以了。

用 Ant 和 Maven 進(jìn)行測(cè)試
這個(gè)像 JUnit 一樣的框架的美妙之處還在于,它可以把整套測(cè)試作為 build 的一部分運(yùn)行,不需要人工進(jìn)行干預(yù)。隨著越來(lái)越多的人把測(cè)試用例加入代碼基(code base),整體的測(cè)試套件日益增長(zhǎng),形成一個(gè)極好的回歸平臺(tái)(regression platform)。更妙的是,Ant 和 Maven 這樣的 build 框架已經(jīng)加入了報(bào)告特性,可以歸納 Junit 批處理任務(wù)運(yùn)行的結(jié)果。

把一組 Groovy 測(cè)試用例整合到某一個(gè)構(gòu)建中的最簡(jiǎn)單的方法是把它們編譯成普通的 Java 字節(jié)碼,然后把它們包含在 Ant 和 Maven 提供的標(biāo)準(zhǔn)的 Junit 批命令中。幸運(yùn)的是,Groovy 提供了一個(gè) Ant 標(biāo)簽,能夠把未編譯的 Groovy 腳本集成到字節(jié)碼中,這樣,把腳本轉(zhuǎn)換成有用的字節(jié)碼的處理工作就變得再簡(jiǎn)單不過(guò)。例如,如果正在使用 Maven 進(jìn)行構(gòu)建工作,那么只需在maven.xml 文件中添加兩個(gè)新的目標(biāo)、在 project.xml 中添加兩個(gè)新的相關(guān)性、在 build.properties 文件中添加一個(gè)簡(jiǎn)單的標(biāo)志就可以了。

我要從更新 maven.xml 文件開(kāi)始,用新的目標(biāo)來(lái)編譯示例腳本,如清單 8 所示:

清單 8. 定義 Groovyc 目標(biāo)的新 maven.xml 文件

 <goal name="run-groovyc" prereqs="java:compile,test:compile">
   
   <path id="groovy.classpath">
     <pathelement path="${maven.build.dest}"/>
     <pathelement path="target/classes"/>
     <pathelement path="target/test-classes"/>
     <path refid="maven.dependency.classpath"/>
   </path>

 <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc">
    <classpath refid="groovy.classpath"/>
 </taskdef>

 <groovyc destdir="${basedir}/target/test-classes" srcdir="${basedir}/test/groovy" 
          listfiles="true">
	<classpath refid="groovy.classpath"/>
 </groovyc>

 </goal>

上面代碼中發(fā)生了以下幾件事。第一,我定義一個(gè)名為 run-groovyc 的新目標(biāo)。該目標(biāo)有兩個(gè)前提條件, java:compile 編譯示例源代碼, test:compile 編譯普通的 Java-JUnit 類(lèi)。我還用 <path> 標(biāo)簽創(chuàng)建了一個(gè) classpath。在該例中,classpath 把 build 目錄(保存編譯后的源文件)和與它相關(guān)的所有依存關(guān)系(即 JAR 文件)整合在一起。接著,我還用 <taskdef> Ant 標(biāo)簽定義了 groovyc 任務(wù)。

而且,請(qǐng)您注意我在 classpath 中是如何告訴 Maven 到哪里去找 org.codehaus.groovy.ant.Groovyc 這個(gè)類(lèi)。在示例的最后一行,我定義了 <groovyc> 標(biāo)簽,它會(huì)編譯在 test/groovy 目錄中發(fā)現(xiàn)的全部 Groovy 腳本,并把生成的 .class 文件放在 target/test-classes 目錄中。

一些重要細(xì)節(jié)
為了編譯 Groovy 腳本,并運(yùn)行生成的字節(jié)碼,我必須要通過(guò) project.xml 文件定義兩個(gè)新的依存關(guān)系( groovyasm),如清單 9 所示:

清單 9. project.xml 文件中的新的依存關(guān)系

  <dependency>
    <groupId>groovy</groupId>
    <id>groovy</id>
    <version>1.0-beta-6</version>
  </dependency>

  <dependency>
    <groupId>asm</groupId>
    <id>asm</id>
    <version>1.4.1</version>
  </dependency>

一旦將腳本編譯成普遍的 Java 字節(jié)碼,那么任何標(biāo)準(zhǔn)的 JUnit 運(yùn)行器就都能運(yùn)行它們。因?yàn)?Ant 和 Maven 都擁有 JUnit 運(yùn)行器標(biāo)簽,所以下一步就是讓 JUnit 挑選新編譯的 Groovy 腳本。而且,因?yàn)?Maven 的 JUnit 運(yùn)行器使用模式匹配來(lái)查找要運(yùn)行的測(cè)試套件,所以需要在 build.properties 文件中添加一個(gè)特殊標(biāo)記,如清單 10 所示,該標(biāo)記告訴 Maven 去搜索類(lèi)而不是搜索 .java 文件。

清單 10. Maven 項(xiàng)目的 build.properties 文件

 maven.test.search.classdir = true

最后,我在 maven.xml 文件中定義了一個(gè)測(cè)試目標(biāo)( goal),如清單 11 所示。這樣做可以確保 在單元測(cè)試運(yùn)行之前,使用新的 run-groovyc 目標(biāo)編譯 Groovy 腳本。

清單 11. maven.xml 的新目標(biāo)

  <goal name="test">
    <attainGoal name="run-groovyc"/>
    <attainGoal name="test:test"/>    	
  </goal>

最后一個(gè),但并非最不重要
有了新定義的兩個(gè)目標(biāo)(一個(gè)用來(lái)編譯腳本,另外一個(gè)用來(lái)運(yùn)行 Java 和 Groovy 組合而成的單元測(cè)試),剩下的事就只有運(yùn)行它們,檢查是不是每件事都順利運(yùn)行!

在清單 12 中,您可以看到,當(dāng)我運(yùn)行 Maven,給 test 傳遞目標(biāo)之后,會(huì)發(fā)生了什么,它首先包含 run-groovyc 目標(biāo)(而該目標(biāo)恰好還包含 java:compiletest:compile 這兩個(gè)目標(biāo)),然后包含 Maven 中自帶的標(biāo)準(zhǔn)的 test:test 目標(biāo)。請(qǐng)注意觀察目標(biāo) test:test 是如何處理新生成的 Groovy 腳本(在該例中,是新 編譯的 Groovy 腳本) 以及普通的 Java JUnit 測(cè)試。

清單 12. 運(yùn)行新的測(cè)試目標(biāo)

$ ./maven test

test:
java:compile:
    [echo] Compiling to /home/aglover/dev/target/classes
    [javac] Compiling 15 source files to /home/aglover/dev/target/classes

test:compile:
    [javac] Compiling 4 source files to /home/aglover/dev/target/test-classes

run-groovyc:
    [groovyc] Compiling 2 source files to /home/aglover/dev/target/test-classes
    [groovyc] /home/aglover/dev/test/groovy/test/RegexFilterTest.groovy
    [groovyc] /home/aglover/dev/test/groovy/test/SimpleFilterTest.groovy

test:test:    
    [junit] Running test.RegexFilterTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.656 sec    
    [junit] Running test.SimpleFilterTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.609 sec
    [junit] Running test.SimplePackageFilterTest
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.578 sec    
BUILD SUCCESSFUL
Total time: 42 seconds
Finished at: Tue Sep 21 17:37:08 EDT 2004

結(jié)束語(yǔ)
實(shí)戰(zhàn) Groovy 系列的第一期中,您學(xué)習(xí)了 Groovy 這個(gè)令人興奮的腳本語(yǔ)言最實(shí)用的應(yīng)用當(dāng)中的一個(gè)。對(duì)于越來(lái)越多開(kāi)發(fā)人員而言,單元測(cè)試是開(kāi)發(fā)過(guò)程的重要組成部分;而使用 Groovy 和 JUnit 對(duì) Java 代碼進(jìn)行測(cè)試就變成了輕而易舉的事情。

Groovy 簡(jiǎn)單的語(yǔ)法、內(nèi)部的靈活性,使其成為迅速編寫(xiě)有效的 JUnit 測(cè)試、將測(cè)試整合到自動(dòng)編譯中的一個(gè)優(yōu)秀平臺(tái)。對(duì)于像我一樣為代碼質(zhì)量發(fā)狂的人來(lái)說(shuō),這種組合極大地減少了我的 神經(jīng)緊張,還讓我可以得到我想做得最好的東西:編寫(xiě)“防彈”軟件。快點(diǎn)行動(dòng)吧。

因?yàn)檫@是一個(gè)新的系列,所以我非常希望您能一起來(lái)推動(dòng)它前進(jìn)。如果您對(duì) Groovy 有什么想了解的事情,請(qǐng) 發(fā)郵件給我,讓我知道您的要求!我希望您會(huì)繼續(xù)支持本系列的下一期,在下期中,我將介紹用 Groovy 進(jìn)行 Ant 腳本編程。