診斷 Java 代碼:單元測試與自動化代碼分析協同工作

發表于:2007-05-05來源:作者:點擊數: 標簽:代碼自動化java測試單元
單元測試 和靜態分析通常被看作是有助于確保程序的正確性的互不相干的方法。本文研究了這兩種方法之間的關系,并討論了構成每種方法 工作 構架的工具如何相得益彰。特別地,Eric Allen 討論了一些可用而又令人興奮的新應用程序,這些應用程序允許您進一步提

單元測試和靜態分析通常被看作是有助于確保程序的正確性的互不相干的方法。本文研究了這兩種方法之間的關系,并討論了構成每種方法工作構架的工具如何相得益彰。特別地,Eric Allen 討論了一些可用而又令人興奮的新應用程序,這些應用程序允許您進一步提升您的單元測試。

這是一場古老的爭論 — 哪種方法對產生健壯代碼更有價值:測試還是靜態分析和驗證?您會在程序員的日常工作中聽到這種爭論,尤其是在極端編程(Extreme Programming)CC%B3');" target="_self">論壇上。(請參閱我們由 Roy Miller 主持的 XP 論壇。)

支持靜態分析(包括類型檢查)的主要論據是:其結果適用程序所有可能的運行,而通過單元測試只能保證被測試的組件(在測試它們的平臺上)只適用測試組件的特定輸入。

支持單元測試的主要論據是它更容易處理。您可以測試程序的許多約束,這些約束遠遠超出了同期的靜態分析工具所能達到的范圍。

請允許我在此冒昧地說一句:我認為將這兩種工具看作對立的是一個錯誤。每種工具都有助于構建更健壯的程序。實際上,它們可以通過非常強大的方式進行互補。

每種工具都有各自的長處,對于補充另一種工具特別有用:

  • 單元測試能顯示執行的常用路徑,從而顯示程序是如何運行的。
  • 分析工具能檢查單元測試提供的覆蓋范圍。

讓我們研究這其中的每個屬性,并討論一些可幫助您將其長處帶給其它方法的工具。

顯示常用執行路徑的單元測試
單元測試套件提供了程序組件的示例用法的穩固基礎。通過檢查測試運行時程序是如何運作的,分析工具可以就開發人員希望在程序中保持的不變量進行試探性推測(就和程序員閱讀單元測試所做的一樣)。

還有另一種方法,其中單元測試可以是一種可執行的文檔形式。在從單元測試的運行中從特殊到一般地推斷出推測性不變量之后,分析工具可以嘗試從一般到特殊地驗證不變量的存在,或者它可以利用可在運行時檢查的斷言注釋該代碼。

在任何一種情況下,在該工具做任何其它工作之前,最好向用戶返回推測的不變量集的報告,以詢問用戶真正想要哪些不變量。順便提一下,如果此類工具向用戶報告了許多他們不想要的不變量,這可能是單元測試出了問題的信號 — 例如,它們不夠一般。

可用這種方式與單元測試一起使用的工具是 Daikon,它是一款來自 MIT 的 Mike Ernst 的程序分析小組的免費的、試驗性的工具。Daikon 分析程序的運行(例如單元測試的運行),并嘗試推測不變量。然后它詢問用戶是否想要這些不變量,并將用戶想要的不變量作為斷言插入程序。

例如,假定我們編寫一個向量(Vector)的適配器,該適配器實現接口 Sequence,該接口包含用于檢索元素的方法 lookup 和用于將元素放在向量末尾的方法 insert。方法 lookup 帶有一個索引 i,用來訪問它所包含的向量。

假定該數組的長度存儲在字段 length 中。通過維護適配器中的長度,我們可以不通知向量本身就將元素從其尾部刪除。

讓我們為這個假想的簡單適配器編寫一個簡單的測試用例

清單 1. 向量容器中簡單查找方法的測試用例

clearcase/" target="_blank" >cccccc height=17>import junit.framework.TestCase;

public class VectorAdapterTest extends TestCase {
public VectorAdapterTest(String name) {
super(name);
}

public void testLookupAndInsert() {
VectorAdapter v = new VectorAdapter();
v.insert("this");
v.insert("is");
v.insert("a");
v.insert("test");
assertEquals("Retrieved and inserted elements don't match",
"a",
v.lookup(2));
}
}

然后我們可以實現我們的適配器以通過這個測試,如下所示:

清單 2. 類 VectorAdapter

import java.util.Vector;

public class VectorAdapter implements Sequence {
private Vector values = new Vector();
private int length = 0;

public void insert(Object o) {
length += 1;
values.addElement(o);
}

public Object lookup(int i) {
return values.elementAt(i);
}
}

interface Sequence {
public void insert(Object o);
public Object lookup(int i);
}

當 Daikon 在這段代碼上運行時,它可能推斷:對于方法 lookup,i 總是小于 length。Daikon 可能從單元測試中推斷出這一點,并向我們的方法報告一條前置條件:i < length。

然后程序員可以檢查 Daikon 報告的不變量,從而更好地了解其測試覆蓋程序的范圍到底怎么樣。例如,如果 Daikon 開始推斷出大量不想要的不變量,這意味著單元測試只是用不具代表性的可能的程序輸入的子集檢測了程序。

盡管 Daikon 是用 Java 語言編寫的,但它需要用 C++ 編寫的前端,這削弱了它原有的可移植性。盡管如此,還是可以在線獲得針對許多主要平臺的前端構建。此外,Daikon 團隊也打算添加其它平臺所需要的構建。

(您可以在參考資料一節找到關于 Daikon 的下載信息和更多內容。)

可以檢查單元測試覆蓋范圍的分析工具
分析工具可以幫助程序員構建健壯的單元測試套件。迄今為止,完成這一工作主要有兩種方法:

  • 使用靜態分析以嘗試自動生成單元測試套件
  • 使用靜態分析來確定單元測試套件對程序功能的覆蓋范圍到底怎么樣

目前有幾種試圖自動從代碼產生單元測試的免費工具,但大多數擔任這項任務的免費工具還處于起步階段。其中一些比較有希望的是 JUnitDoclet 和 JUB(“JUnit test case Builder”的縮寫),可在 SourceForge 上得到它們(參考資料一節提供了它們的鏈接)。

關于這些類型的工具,要牢記的要點是:最適宜應用于通過測試更新舊代碼。當構建新項目時,它們的作用不大。

為什么會這樣呢?因為新項目應該與項目上的單元測試是一前一后構建的。開發單元測試是構建設計的強有力的方法;針對組件的 API 就是在編寫測試時隱式地為它們設計的。此外,以這種風格進行設計向設計師提供了即時的反饋。糟糕的設計將非常難于編寫測試!并且,任何分析工具在確定為程序編寫什么測試這方面,都很難做得象設計師那樣好。

第二種分析工具分析程序及其單元測試,并確定測試能在多大范圍內覆蓋程序。與剛才提到的第一類工具不同,此類工具對每個項目都是有用的。實際上,極端編程團隊可以考慮將此類工具集成到他們的代碼提交過程中。那么,他們不僅能夠防止代碼在通過所有測試之前被提交,而且可以防止代碼在未經測試的情況下提交!不僅懶惰會導致測試覆蓋范圍偏小,錯誤也可能導致同樣后果,因此,此類強制措施對任何技能(和完整性)級別的程序員都有用。

Clover 是一種可以執行此類分析的新的并且特別有希望的工具。Clover 是 Ant 的插件,Ant 是 make 的流行的、全 Java 的替代物。Clover 是商業工具,但它可以免費用于開放源碼項目。

Clover 分兩階段過程進行工作。首先,它在編譯時檢測代碼。然后,在測試時將有關測試的運行信息寫到用來生成報告的數據庫中(通過 GUI、網頁或在控制臺中)。

將 Clover 集成到使用 Ant 的現有項目中很簡單。這涉及調整項目的 build.xml 文件以添加幾個在編譯、記錄測試和生成報表期間檢測代碼的目標。例如,假定我們有一個帶構建和編譯目標的 build.xml 文件。我們所必須做的全部工作是將 Clover JAR 文件放到我們的 Ant 庫目錄中,并如下所示擴展 build.xml 文件(Clover 用戶指南中提供了這些和類似于 Ant 目標的信息;為了方便,我在這里包括了它們):

清單 3. 擴充 Ant build.xml 文件以使用 Clover

<property name="clover.initstring" value="/tmp/mycoverage.db"/>

<target name="with.clover">
<property name="build.compiler"
value="org.apache.tools.ant.taskdefs.CloverCompilerAdapter"/>
</target>

<path id="clover.classpath">
<pathelement path="<CLOVER_HOME>/lib/clover.jar"/>
<pathelement path="<CLOVER_HOME>/lib/velocity.jar"/>
</path>

<target name="clover.report">
<java classname="com.cortexeb.tools.clover.reporters.html.HtmlReporter">
<arg line="--outputdir /tmp/clover_html --showSrc --initstring
$\{clover.initstring\} --title 'My Project'"/>
<classpath refid="clover.classpath"/>
</java>
</target>

特性 clover.initstring 指定了一個文件,有關 Clover 覆蓋范圍的數據將寫入這個文件中。目標 with.clover 用來在執行其它目標(如 compiletest)時打開 Clover。clover.report 目標用來接收累積的覆蓋范圍數據并生成報告。

在上面的代碼中,我們將生成 HTML 報告。我們也可以生成文本報告(對于提供給腳本以確定測試的覆蓋范圍是否可接受非常有用)和基于 Swing 的報告。

設置 clover.classpath 是必要的,以便報告生成器目標知道到哪里找到它所需要的所有類。但是,放在類路徑中的第二個 JAR 文件(velocity.jar)只有在生成 HTML 報告時才是必需的。一旦完成了上述工作,就可以用以下命令生成 Clover 報告:

$ ant with.clover compile test
$ ant clover.report

就是這么簡單。請查閱流行的編碼工具 JBoss 和 Ant 的在線 Clover 報告,以獲取一些樣本輸出。(您可以在參考資料一節找到更多有關這些內容的下載信息和其它信息。)

兩種方法的結合
本文中討論的工具突出了一些可以將程序分析和單元測試一起使用的方法,以提供比單獨執行任何一種方法都更有效的不變量檢測。這些技術只代表了所有可能技術的冰山一角。

將來,其它工具可能會提供更強有力的單元測試能力。例如,類型推斷(type-inference)引擎和優化編譯器可以從現有的單元測試推斷線索、UML 生成工具可以從測試構造各種圖表(不僅是類圖)等等。對于合并這些方法以獲得更佳的代碼構建和故障診斷而言,還存在著巨大的空間可以進行創造性開發和實驗。

請記住每種方法的屬性及其長處:

  • 單元測試能夠演示程序在特定的運行期是如何運轉的;還能說明執行的常用路徑。

  • 分析工具能夠檢查所有可能運行的程序的某些特定屬性。

每種方法的長處都可以用來彌補另一種方法的潛在弱點。

下一次,我們將研究增強的單元測試的另一條路徑,并了解一些最新的工具,它們可用于幫助您在 GUI 上開發單元測試。

參考資料

  • 請單擊文章頂部或底部的討論參與本文的論壇。

  • 了解所有關于 Daikon(用 C/C++ 和 Java 前端進行動態不變量檢測的原型實現)的內容,并從 Daikon 網站下載它。

  • 請查閱 Clover user guide 以獲取更多關于該工具的信息,該工具用來發現未被執行的代碼部分,并確定何處的代碼未經足夠測試。

  • Clover 與 Ant(基于 Java 的構建工具)緊密地集成在一起,如果您還沒有副本,請下載一個。

  • JUnitDoclet 是使用 XDoclet 的開放源碼單元測試生成工具。

  • 單元測試是極端編程的關鍵實踐。在 Roy Miller 的 developerWorks 專欄 Demystifying Extreme Programming 中了解關于所有極端編程實踐的更多信息。您也可以從 developerWorks Demystifying Extreme Programming 論壇獲得 Roy 關于您 XP 問題的解答。

  • 另一種開放源碼生成工具是 JUB(JUnit 測試用例構建器),可從 SourceForge 獲得。

  • 可以在 JUnit 網站上找到更多擴充單元測試的工具。

  • Nicholas Lesiecki 在其文章“Test flexibly with AspectJ and mock objects”(developerWorks,2002 年 5 月)中介紹了關于測試用例獨立性的問題,并向我們展示了如何使用仿真對象和 AspectJ 來開發準確而又強壯的單元測試。

  • Erik Hatcher 在其文章“Automating the build and test process”(developerWorks,2001 年 8 月)中,向您展示了他是如何修改流行的 Ant1.3 和 JUnit 測試框架,以實現構建和測試過程的完整的、定制的自動化。

  • WebSphere 開發者園地在“Application Quality Assurance:Unit Testing”中提供了一篇關于用 JUnit 進行單元測試的好文章。

  • Debugging and Unit-Testing Server-Side Web Applications”也來自 WebSphere 開發者園地,它描述了包含迭代調試和交互式單元測試的服務器端 Web 開發方案。

  • 下載 DrJava,這是一個免費的、輕量型的開放源碼 Java IDE,具有集成的讀-計算-打?。╮ead-eval-print)循環、調試器和 JUnit 支持。

  • 閱讀 Eric Allen 的診斷 Java 代碼專欄的所有文章,包括“Designing‘testable'applications”。

  • developerWorks Java 技術專區上可找到關于 Java 技術的數百篇文章和教程。
關于作者
Eric Allen 擁有康奈爾大學(Cornell University)計算機科學和數學學士學位,并且是萊斯大學 Java 編程語言團隊的博士研究生。在返回萊斯完成學位之前,Eric 是 Cycorp, Inc. 的主要 Java 軟件開發人員。他還主持 JavaWorld 的 Java 初學者論壇。他的研究涉及源代碼和字節碼級別的 Java 語言的語義模型和靜態分析工具的開發。Eric 是萊斯的 NextGen 編程語言(具有附加語言特性的 Java 語言擴展)的實驗性編譯器的主要開發人員,并且是 DrJava(為初學者設計的開放源碼 Java IDE)的項目經理??赏ㄟ^ eallen@cs.rice.edu 與 Eric 聯系。

原文轉自:http://www.anti-gravitydesign.com

...

熱門標簽

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97