日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(二)单元测试利器 JUnit 4

發布時間:2023/11/30 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (二)单元测试利器 JUnit 4 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

JUnit 深入
??????? 當然,JUnit 提供的功能決不僅僅如此簡單,在接下來的內容中,我們會看到 JUnit 中很多有用的特性,掌握它們對您靈活的編寫單元測試代碼非常有幫助。
Fixture
??????? 何謂 Fixture?它是指在執行一個或者多個測試方法時需要的一系列公共資源或者數據,例如測試環境,測試數據等等。在編寫單元測試的過程中,您會發現在大部分的測試方法在進行真正的測試之前都需要做大量的鋪墊——為設計準備 Fixture 而忙碌。這些鋪墊過程占據的代碼往往比真正測試的代碼多得多,而且這個比率隨著測試的復雜程度的增加而遞增。當多個測試方法都需要做同樣的鋪墊時,重復代碼的“壞味道”便在測試代碼中彌漫開來。這股“壞味道”會弄臟您的代碼,還會因為疏忽造成錯誤,應該使用一些手段來根除它。
??????? JUnit 專門提供了設置公共 Fixture 的方法,同一測試類中的所有測試方法都可以共用它來初始化 Fixture 和注銷 Fixture。和編寫 JUnit 測試方法一樣,公共 Fixture 的設置也很簡單,您只需要:
1.?使用注解 org,junit.Before 修飾用于初始化 Fixture 的方法。
2.?使用注解 org.junit.After 修飾用于注銷 Fixture 的方法。
3.?保證這兩種方法都使用 public void 修飾,而且不能帶有任何參數。
??????? 遵循上面的三條原則,編寫出的代碼大體是這個樣子:
//初始化Fixture方法
@Before public void init(){……}

//注銷Fixture方法
@After public void destroy(){……}

??????? 這樣,在每一個測試方法執行之前,JUnit 會保證 init 方法已經提前初始化測試環境,而當此測試方法執行完畢之后,JUnit 又會調用 destroy 方法注銷測試環境。注意是每一個測試方法的執行都會觸發對公共 Fixture 的設置,也就是說使用注解 Before 或者 After 修飾的公共 Fixture 設置方法是方法級別的(圖5)。這樣便可以保證各個獨立的測試之間互不干擾,以免其它測試代碼修改測試環境或者測試數據影響到其它測試代碼的準確性。

圖5 方法級別 Fixture 執行示意圖

???????????????????????????????????????????
??????? 可是,這種 Fixture 設置方式還是引來了批評,因為它效率低下,特別是在設置 Fixture 非常耗時的情況下(例如設置數據庫鏈接)。而且對于不會發生變化的測試環境或者測試數據來說,是不會影響到測試方法的執行結果的,也就沒有必要針對每一個測試方法重新設置一次 Fixture。因此在 JUnit 4 中引入了類級別的 Fixture 設置方法,編寫規范如下:
1.?使用注解 org,junit.BeforeClass 修飾用于初始化 Fixture 的方法。
2.?使用注解 org.junit.AfterClass 修飾用于注銷 Fixture 的方法。
3.?保證這兩種方法都使用 public static void 修飾,而且不能帶有任何參數。
??????? 類級別的 Fixture 僅會在測試類中所有測試方法執行之前執行初始化,并在全部測試方法測試完畢之后執行注銷方法(圖6)。代碼范本如下:
//類級別Fixture初始化方法
@BeforeClass public static void dbInit(){……}
?
//類級別Fixture注銷方法
?@AfterClass public static void dbClose(){……}


圖6 類級別 Fixture 執行示意圖

????????????????
異常以及時間測試
??????? 注解 org.junit.Test 中有兩個非常有用的參數:expected 和 timeout。參數 expected 代表測試方法期望拋出指定的異常,如果運行測試并沒有拋出這個異常,則 JUnit 會認為這個測試沒有通過。這為驗證被測試方法在錯誤的情況下是否會拋出預定的異常提供了便利。舉例來說,方法 supportDBChecker 用于檢查用戶使用的數據庫版本是否在系統的支持的范圍之內,如果用戶使用了不被支持的數據庫版本,則會拋出運行時異常 UnsupportedDBVersionException。測試方法 supportDBChecker 在數據庫版本不支持時是否會拋出指定異常的單元測試方法大體如下:
@Test(expected=UnsupportedDBVersionException.class)
?public void unsupportedDBCheck(){
??……
}

??????? 注解 org.junit.Test 的另一個參數 timeout,指定被測試方法被允許運行的最長時間應該是多少,如果測試方法運行時間超過了指定的毫秒數,則JUnit認為測試失敗。這個參數對于性能測試有一定的幫助。例如,如果解析一份自定義的 XML 文檔花費了多于 1 秒的時間,就需要重新考慮 XML 結構的設計,那單元測試方法可以這樣來寫:
@Test(timeout=1000)
?public void selfXMLReader(){
??……
}

忽略測試方法
??????? JUnit 提供注解 org.junit.Ignore 用于暫時忽略某個測試方法,因為有時候由于測試環境受限,并不能保證每一個測試方法都能正確運行。例如下面的代碼便表示由于沒有了數據庫鏈接,提示 JUnit 忽略測試方法 unsupportedDBCheck:
@ Ignore(“db is down”)
@Test(expected=UnsupportedDBVersionException.class)
?public void unsupportedDBCheck(){
??……
}

??????? 但是一定要小心。注解 org.junit.Ignore 只能用于暫時的忽略測試,如果需要永遠忽略這些測試,一定要確認被測試代碼不再需要這些測試方法,以免忽略必要的測試點。
測試運行器
??????? 又一個新概念出現了——測試運行器,JUnit 中所有的測試方法都是由它負責執行的。JUnit 為單元測試提供了默認的測試運行器,但 JUnit 并沒有限制您必須使用默認的運行器。相反,您不僅可以定制自己的運行器(所有的運行器都繼承自 org.junit.runner.Runner),而且還可以為每一個測試類指定使用某個具體的運行器。指定方法也很簡單,使用注解 org.junit.runner.RunWith 在測試類上顯式的聲明要使用的運行器即可:
@RunWith(CustomTestRunner.class)
public class TestWordDealUtil {
……
}

??????? 顯而易見,如果測試類沒有顯式的聲明使用哪一個測試運行器,JUnit 會啟動默認的測試運行器執行測試類(比如上面提及的單元測試代碼)。一般情況下,默認測試運行器可以應對絕大多數的單元測試要求;當使用 JUnit 提供的一些高級特性(例如即將介紹的兩個特性)或者針對特殊需求定制 JUnit 測試方式時,顯式的聲明測試運行器就必不可少了。
測試套件
??????? 在實際項目中,隨著項目進度的開展,單元測試類會越來越多,可是直到現在我們還只會一個一個的單獨運行測試類,這在實際項目實踐中肯定是不可行的。為了解決這個問題,JUnit 提供了一種批量運行測試類的方法,叫做測試套件。這樣,每次需要驗證系統功能正確性時,只執行一個或幾個測試套件便可以了。測試套件的寫法非常簡單,您只需要遵循以下規則:
1.?創建一個空類作為測試套件的入口。
2.?使用注解 org.junit.runner.RunWith 和 org.junit.runners.Suite.SuiteClasses 修飾這個空類。
3.?將 org.junit.runners.Suite 作為參數傳入注解 RunWith,以提示 JUnit 為此類使用套件運行器執行。
4.?將需要放入此測試套件的測試類組成數組作為注解 SuiteClasses 的參數。
5.?保證這個空類使用 public 修飾,而且存在公開的不帶有任何參數的構造函數。
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 {
}

??????? 上例代碼中,我們將前文提到的測試類 TestWordDealUtil 放入了測試套件 RunAllUtilTestsSuite 中,在 Eclipse 中運行測試套件,可以看到測試類 TestWordDealUtil 被調用執行了。測試套件中不僅可以包含基本的測試類,而且可以包含其它的測試套件,這樣可以很方便的分層管理不同模塊的單元測試代碼。但是,您一定要保證測試套件之間沒有循環包含關系,否則無盡的循環就會出現在您的面前……。
參數化測試
??????? 回顧一下我們在小節“JUnit 初體驗”中舉的實例。為了保證單元測試的嚴謹性,我們模擬了不同類型的字符串來測試方法的處理能力,為此我們編寫大量的單元測試方法。可是這些測試方法都是大同小異:代碼結構都是相同的,不同的僅僅是測試數據和期望值。有沒有更好的方法將測試方法中相同的代碼結構提取出來,提高代碼的重用度,減少復制粘貼代碼的煩惱?在以前的 JUnit 版本上,并沒有好的解決方法,而現在您可以使用 JUnit 提供的參數化測試方式應對這個問題。
參數化測試的編寫稍微有點麻煩(當然這是相對于 JUnit 中其它特性而言):
1.?為準備使用參數化測試的測試類指定特殊的運行器 org.junit.runners.Parameterized。
2.?為測試類聲明幾個變量,分別用于存放期望值和測試所用數據。
3.?為測試類聲明一個使用注解 org.junit.runners.Parameterized.Parameters 修飾的,返回值為 java.util.Collection 的公共靜態方法,并在此方法中初始化所有需要測試的參數對。
4.?為測試類聲明一個帶有參數的公共構造函數,并在其中為第二個環節中聲明的幾個變量賦值。
5.?編寫測試方法,使用定義的變量作為參數進行測試。
我們按照這個標準,重新改造一番我們的單元測試代碼:
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));
??}
}

??????? 很明顯,代碼瘦身了。在靜態方法 words 中,我們使用二維數組來構建測試所需要的參數列表,其中每個數組中的元素的放置順序并沒有什么要求,只要和構造函數中的順序保持一致就可以了。現在如果再增加一種測試情況,只需要在靜態方法 words 中添加相應的數組即可,不再需要復制粘貼出一個新的方法出來了。
JUnit 和 Ant
??????? 隨著項目的進展,項目的規模在不斷的膨脹,為了保證項目的質量,有計劃的執行全面的單元測試是非常有必要的。但單靠JUnit提供的測試套件很難勝任這項工作,因為項目中單元測試類的個數在不停的增加,測試套件卻無法動態的識別新加入的單元測試類,需要手動修改測試套件,這是一個很容易遺忘得步驟,稍有疏忽就會影響全面單元測試的覆蓋率。
??????? 當然解決的方法有多種多樣,其中將 JUnit 與構建利器 Ant 結合使用可以很簡單的解決這個問題。Ant —— 備受贊譽的 Java 構建工具。它憑借出色的易用性、平臺無關性以及對項目自動測試和自動部署的支持,成為眾多項目構建過程中不可或缺的獨立工具,并已經成為事實上的標準。Ant 內置了對 JUnit 的支持,它提供了兩個 Task:junit 和 junitreport,分別用于執行 JUnit 單元測試和生成測試結果報告。使用這兩個 Task 編寫構建腳本,可以很簡單的完成每次全面單元測試的任務。
不過,在使用 Ant 運行 JUnit 之前,您需要稍作一些配置。打開 Eclipse 首選項界面,選擇 Ant -> Runtime 首選項(見圖7),將 JUnit 4.1 的 JAR 文件添加到 Classpath Tab 頁中的 Global Entries 設置項里。記得檢查一下 Ant Home Entries 設置項中的 Ant 版本是否在 1.7.0 之上,如果不是請替換為最新版本的 Ant JAR 文件。

圖7 Ant Runtime 首選項

????????????????????

??????? 剩下的工作就是要編寫 Ant 構建腳本 build.xml。雖然這個過程稍嫌繁瑣,但這是一件一勞永逸的事情。現在我們就把前面編寫的測試用例都放置到 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 構建腳本中的核心內容,其它 target 都是為它的執行提供前期服務。Task junit 會尋找輸出目錄下所有命名以“Test”開頭的 class 文件,并執行它們。緊接著 Task junitreport 會將執行結果生成 HTML 格式的測試報告(圖8)放置在“report folder”下。
??????? 為整個項目的單元測試類確定一種命名風格。不僅是出于區分類別的考慮,這為 Ant 批量執行單元測試也非常有幫助,比如前面例子中的測試類都已“Test”打頭,而測試套件則以“Suite”結尾等等。

圖8 junitreport 生成的測試報告

??????????????????????
??????? 現在執行一次全面的單元測試變得非常簡單了,只需要運行一下 Ant 構建腳本,就可以走完所有流程,并能得到一份詳盡的測試報告。您可以在 Ant 在線手冊 中獲得上面提及的每一個 Ant 內置 task 的使用細節。
總結
??????? 隨著越來越多的開發人員開始認同并接受極限編程(XP)的思想,單元測試的作用在軟件工程中變得越來越重要。本文旨在將最新的單元測試工具 JUnit 4 介紹給您,以及如何結合 IDE Eclipse 和構建工具 Ant 創建自動化單元測試方案。并且還期望您能夠通過本文“感染”一些好的單元測試意識,因為 JUnit 本身僅僅是一份工具而已,它的真正優勢來自于它的思想和技術

轉載于:https://www.cnblogs.com/sunshine-study/p/3652479.html

總結

以上是生活随笔為你收集整理的(二)单元测试利器 JUnit 4的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。