程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 診斷Java代碼: 單元測試與自動化代碼分析協同工作

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

編輯:關於JAVA

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

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

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

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

單元測試能顯示執行的常用路徑,從而顯示程序是如何運行的。

分析工具能檢查單元測試提供的覆蓋范圍。

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

顯示常用執行路徑的單元測試

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

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

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

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

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

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

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

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

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 團隊也打算添加其它平台所需要的構建。

可以檢查單元測試覆蓋范圍的分析工具

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

使用靜態分析以嘗試自動生成單元測試套件

使用靜態分析來確定單元測試套件對程序功能的覆蓋范圍到底怎麼樣

目前有幾種試圖自動從代碼產生單元測試的免費工具,但大多數擔任這項任務的免費工具還處於起步階段。

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

為什麼會這樣呢?因為新項目應該與項目上的單元測試是一前一後構建的。開發單元測試是構建設計的強有力的方法;針對組件的 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 用來在執行其它目標(如 compile 和 test )時打開 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 上開發單元測試。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved