Demo002 IDEA中Junit單元測試的使用(初級篇)


推薦JUnit視頻教程:JUnit—Java單元測試必備工具

1.基本理論

1.1 單元測試

單元測試又稱模塊測試,屬於白盒測試,是最小單位的測試。模塊分為程序模塊和功能模塊。功能模塊指實現了一個完整功能的模塊(單元),一個完整的程序單元具備輸入、加工和輸出三個環節。而且每個程序單元都應該有正規的規格說明,使之對其輸入、加工和輸出的關系做出名明確的描述。

JUnit是一個回歸測試框架(regression testing framework)。Junit測試是程序員測試,即所謂白盒測試,因為程序員知道被測試的軟件如何(How)完成功能和完成什么樣(What)的功能。Junit是一套框架,繼承TestCase類,就可以用Junit進行自動測試了。

1.2 什么是Junit

①JUnit是用於編寫可復用測試集的簡單框架,是xUnit的一個子集。xUnit是一套基於測試驅動開發的測試框架,有PythonUnit、CppUnit、JUnit等。

②Junit測試是程序員測試,即所謂白盒測試,因為程序員知道被測試的軟件如何(How)完成功能和完成什么樣(What)的功能。

③多數Java的開發環境都已經集成了JUnit作為單元測試的工具,比如IDEA,Eclipse等等。

④JUnit官網:Junit

1.3 為什么要使用單元測試

①測試框架可以幫助我們對編寫的程序進行有目的地測試,幫助我們最大限度地避免代碼中的bug,以保證系統的正確性和穩定性。

②很多人對自己寫的代碼,測試時就簡單寫main,然后sysout輸出控制台觀察結果。這樣非常枯燥繁瑣,不規范。缺點:測試方法不能一起運行,測試結果要程序猿自己觀察才可以判斷程序邏輯是否正確。

③JUnit的斷言機制,可以直接將我們的預期結果和程序運行的結果進行一個比對,確保對結果的可預知性。

1.4 測試覆蓋

評測測試過程中已經執行的代碼的多少。

1.5 代碼覆蓋率

代碼的覆蓋程度,一種度量方式。針對代碼的測試覆蓋率有許多種度量方式。

  • 語句覆蓋( StatementCoverage ):也稱為行覆蓋( lin EC overage ) , 段覆蓋(segmentcoverage)和基本塊覆蓋(bASicblockcoverage)。它度量每一個可執行語句是否被執行到了。

  • 判定覆蓋(DecisionCoverage):也被稱為分支覆蓋(branchcoverage),所有邊界覆蓋(alledgescoverage), 基本路徑覆蓋( basispathcoverage ), 判定路徑覆蓋(decisiondecisionpath或DDPtesting)。它度量是否每個 BOOL 型的表達式取值true 和 false 在控制結構中都被測試到了。

  • 條件覆蓋(ConDItionCoverage): 它獨立的度量每一個子表達式,報告每一個子表達式的結果的 true 或 false。這個度量和判定覆蓋(decisioncoverage)相似,但是對控制流更敏感。不過,完全的條件覆蓋並不能保證完全的判定覆蓋。

  • 路徑覆蓋(PathCoverage):也稱為斷言覆蓋(prEDIcatecoverage),它度量了是否函數的每一個可能的分支都被執行了。路徑覆蓋的一個好處是:需要徹底的測試。但有兩個缺點:一是,路徑是以分支的指數級別增加的,例如:一個函數包含 10個 IF 語句,就有 1024 個路徑要測試。如果加入一個 IF 語句,路徑數就達到 2048;二是,許多路徑不可能與執行的數據無關。

  • 循環覆蓋(LOOPCoverage):這個度量報告你是否執行了每個循環體零次、只有一次還是多余一次(連續地)。對於 dowhile循環,循環覆蓋報告你是否執行了每個循環體只有一次還是多余一次(連續地)。這個度量的有價值的方面是確定是否對於 while 循環和 for 循環執行了多於一次,這個信息在其它的覆蓋率報告中是沒有的。

2. IDEA中Junit 4的配置

2.1 環境配置

IDEA中自帶JUnit的jar包(hamcrest-core-1.3.jarjunit.jarjunit-4.12.jar):位於安裝目錄下的lib文件內。即:IntelliJ IDEA 2018.1\lib

IDEA自帶一個JUnit插件, 該插件可以運行JUnit測試文件,但無法自動生成JUnit測試代碼. 如果需要自動生成測試代碼,需要安裝JUnitGenerator V2.0 插件。

安裝方法:File-->settings-->Plguins-->Browse repositories-->輸入JUnit-->選擇JUnit Generator V2.0安裝,安裝好后重啟 IDEA,即可使用。

PS: IDEA自帶的JUnit插件和JUnitGeneratorV2.0插件都要勾選上,若只勾選JUnit可能導致無法自動生成測試文件,若只勾選JUnitGenerator V2.0可能導致生成的測試文件無法運行

2.2 修改配置

  1. 通過模版生成的測試代碼與java類在同一個目錄下,與maven項目標准測試目錄不匹配。修改方法:
    ${SOURCEPATH}/test/${PACKAGE}/${FILENAME} 修改為${SOURCEPATH}/../test/java/${PACKAGE}/${FILENAME}
    有時候會出現一個提示框,提示選擇(general/merge/exixt),選擇general就行

    ${SOURCEPATH} 原類的路徑,就是你自己寫的類
    ${PACKAGE} 原類的包名,java包命名規范就是按照包名一級一級創建的文件夾
    ${filename} 測試類的類名

  2. 修改 Junit4 選項卡中包的申明,把默認的 test 前綴去掉。

  3. 生成的測試文件@since位置Date可能存在亂碼,可配置JUnit模板更改日期格式,不影響程序可直接忽略。修改方法:
    第40行:@since <pre>$Date</pre> 修改為
    @since <pre>$today</pre>

3. 簡單入門單元測試

婁老師的博客文章中講了單元測試,我們根據老師的方法來進行簡單入門單元測試。

什么是單元測試?單元測試(unit testing),是指對軟件中的最小可測試單元進行檢查和驗證。對於單元測試中單元的含義,一般來說,要根據實際情況去判定其具體含義,如Java里單元指一個類。

編程是智力活動,不是打字,編程前要把干什么、如何干想清楚才能把程序寫對、寫好。與目前不少同學一說編程就打開編輯器寫代碼不同,我希望同學們養成一個習慣,當你們想用程序解決問題時,要會寫三種碼:

  • 偽代碼
  • 產品代碼
  • 測試代碼

偽代碼與具體編程語言無關,不要寫與具體編程語言語法相關的語句(如用malloc分配內存,這樣只能用C語言編程了),偽代碼從意圖層面來解決問題,最終,偽代碼產品代碼最自然的、最好的注釋。

需求:我們要在一個Calculator類中來實現簡單計算器的功能。

3.1 偽代碼

設計的偽代碼:

    簡單計算器:
    
    加法:返回兩個數相加后的結果
    
    減法:返回兩個數相減后的結果
    
    乘法:返回兩個數相乘后的結果
    
    除法:除數不為0,返回兩個數相除后的結果
    
    	除數為0,打印異常信息在程序中出錯的位置及原因
    	
    其他 :未完成的模塊例如平方、開方等等
    

3.2 產品代碼

有了偽代碼,用特定語言翻譯一下,就是可用的產品代碼了。
翻譯之后的產品代碼Calculator.java

public class Calculator {
    public int add(int x, int y) { //加法
        return x + y;
    }

    public int sub(int x, int y) { //減法
        return x - y;
    }

    public int mul(int x, int y) { //乘法
        return x * y;
    }

    public int div(int x, int y) { //除法
        return x / y;
    }

    public int div2(int x, int y) { //除法  做了異常判斷
        try {
            int z = x / y;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return x / y;
    }

    public void loop(int x, int y) { //死循環
        for (; ; )
            x = y;
    }

    public void unCompleted(int x, int y) { //未完成的模塊:例如平方、開方等等
        //還在開發中
    }


//    public static void main(String[] args) { // 傳統代碼測試
//        int a = 8;
//        int b = 2;
//        Calculator calculator = new Calculator();
//        System.out.println(calculator.add(a, b));
//        System.out.println(calculator.sub(a, b));
//        System.out.println(calculator.mul(a, b));
//        System.out.println(calculator.div(a, b));
//        System.out.println(calculator.div2(a,0));
//    }
}

在IDEA中創建項目unitTask01,在src 右鍵新建java類com.ryanjie.junit01.Calculator,將上述代碼寫入。

3.3 測試代碼

寫了產品代碼,我們還要寫測試代碼,證明自己的代碼沒有問題。

Java編程時,程序員對類實現的測試叫單元測試。類XXXX的單元測試,我們一般寫建一個XXXXTest的類。

所以,對於Calculator類,我們寫一個CalculatorTest.java的測試模塊,代碼如下:

public class CalculatorTest {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        
        if (calculator.add(8, 2) == 10) {
            System.out.println("Test passed!");
        } else {
            System.out.println("Test failed!");
        }

        if (calculator.sub(8, 2) == 6) {
            System.out.println("Test passed!");
        } else {
            System.out.println("Test failed!");
        }

        if (calculator.mul(8, 2) == 16) {
            System.out.println("Test passed!");
        } else {
            System.out.println("Test failed!");
        }

        if (calculator.div(8, 2) == 4) {
            System.out.println("Test passed!");
        } else {
            System.out.println("Test failed!");
        }
    }
}

這里我們設計了一個測試用例(Test Case),測試用例是為某個特殊目標而編制的一組測試輸入、執行條件以及預期結果,以便測試某個程序路徑或核實是否滿足某個特定需求。這里我們的測試輸入8和2的加減乘除四種運算,預期結果分別為10,6,16,4

我們在Calculator類上單擊鼠標右鍵,在彈出的菜單中選擇Generate(我們可以看到對應鍵盤快捷鍵為Alt+Insert)-> Junit Test -> Junit 4,這時IDEA會自動生成一個test目錄並生成CalculatorTest類,我們先刪除自動生成的代碼,輸入剛才寫的CalculatorTest.java代碼。

在 IDEA中我們把產品代碼放在src目錄中,把測試代碼放在test目錄中,在命令行中我們知道要設置SOURCEPATH環境變量,在IDEA中我們右鍵單擊test目錄,在彈出的菜單中選擇Mark Directory as->Test Sources Root就可以了:(這時test文件夾圖標會變綠)

在IDEA中運行結果如下,測試結果符合預期:

3.4 測試代碼2 (使用Junit)

在剛才自動生成的test目錄下,CalculatorTest.java里代碼如下:

public class CalculatorTest {

    @Before
    public void before() throws Exception {
    }

    @After
    public void after() throws Exception {
    }

    /**
     *
     * Method: add(int x, int y) 
     *
     */
    @Test
    public void testAdd() throws Exception {
    }

    /**
     *
     * Method: sub(int x, int y) 
     *
     */
    @Test
    public void testSub() throws Exception {
    }

    /**
     *
     * Method: mul(int x, int y) 
     *
     */
    @Test
    public void testMul() throws Exception {
 
    }

    /**
     *
     * Method: div(int x, int y) 
     *
     */
    @Test
    public void testDiv() throws Exception {
    }

    /**
     *
     * Method: div2(int x, int y) 
     *
     */
    @Test
    public void testDiv2() throws Exception {
    }

    /**
     *
     * Method: loop(int x, int y) 
     *
     */
    @Test
    public void testLoop() throws Exception {
    }

    /**
     *
     * Method: unCompleted(int x, int y) 
     *
     */
    @Test
    public void testUnCompleted() throws Exception {
    }

}  

我們修改CalculatorTest.java測試模塊,代碼如下:

public class CalculatorTest {
    Calculator calculator;

    @Before
    public void setUp() throws Exception {
        calculator = new Calculator();
    }

    @After
    public void after() throws Exception {
    }

    /**
     *
     * Method: add(int x, int y)
     *
     */
    @Test
    public void testAdd() throws Exception {
        Assert.assertEquals(calculator.add(8,2),10);
    }

    /**
     *
     * Method: sub(int x, int y)
     *
     */
    @Test
    public void testSub() throws Exception {
        Assert.assertEquals(calculator.sub(8,2),6);
    }

    /**
     *
     * Method: mul(int x, int y)
     *
     */
    @Test
    public void testMul() throws Exception {
        Assert.assertEquals(calculator.mul(8,2),16);
    }

    /**
     *
     * Method: div(int x, int y)
     *
     */
    @Test
    public void testDiv() throws Exception {
        Assert.assertEquals(calculator.div(8,2),4);
    }

    /**
     *
     * Method: div2(int x, int y)
     *
     */
    @Ignore
    public void testDiv2() throws Exception {
    }

    /**
     *
     * Method: loop(int x, int y)
     *
     */
    @Ignore
    public void testLoop() throws Exception {
    }

    /**
     *
     * Method: unCompleted(int x, int y)
     *
     */
    @Ignore
    public void testUnCompleted() throws Exception {
    }

}

在IDEA中運行結果如下,測試結果符合預期:

3.5 編輯測試設置

我們可以通過Run ->Edit Configuration或工具欄上的標簽來調整我們測試運行配置:

Configuration選項卡,用戶可以選擇需要運行的測試。例如,您可以從一個類、程序包、測試套件或甚至模式中運行所有的測試。這里的Fork模式讓用戶在一個單獨的進程運行每個測試。

代碼覆蓋測試:

  • 默認情況下使用自己的測試引擎
  • 覆蓋率模式:Tracing會增加消耗,但是測試會更准確

3.6 收集覆蓋率

Run ->Run 'MyClassTest' with Coverage或工具欄上的選項運行特定模式的測試。

當覆蓋模式運行至少一個測試之后,IDE將會在Project工具窗口顯示每個程序包、類的覆蓋率數據,同時在Coverage工具窗和編輯器中也會顯示。

如果用戶添加另一個方法到junit01,並運行覆蓋率測junit01,就會發現,沒有被測試覆蓋到的代碼都將高亮顯示為紅色。覆蓋的代碼顏色則是綠色。如果一些代碼是只覆蓋部分,那沒將顯示為黃色。

本文代碼:UnitTask01


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM