1. 程式人生 > >單元測試整理(二)——斷言篇,首個單元測試程式

單元測試整理(二)——斷言篇,首個單元測試程式

引子

單元測試主要是用來驗證所測程式碼是否和程式設計師的期望一致,如下所示,在實際操作中,我們可以編寫一個函式assertTure()來驗證預期條件是否滿足,也可以進一步編寫一個函式assertEquals()來判斷兩個數是否相等。事實上諸如此類判斷函式,我們稱之為斷言。

public void assertTrue(boolean condition){
    if(!condition){
        abort();
    }
}
--------------------------------------------
int a = 5;
//如果由於某種原因,當呼叫assertTrue()的時候,a並不等於5,上面程式將終止
assertTure(a == 5);
public void assertEquals(int a, int b){
    assertTrue(a == b);
}

斷言 是一種放在程式中的一階邏輯(如一個結果為真或是假的邏輯判斷式),目的是為了標示與驗證程式開發者預期的結果-當程式執行到斷言的位置時,對應的斷言應該為真。若斷言不為真時,程式會中止執行,並給出錯誤訊息1

常用斷言

序號 方法描述 描述
1 void assertEquals(boolean expected, boolean actual) 檢查兩個變數或者等式是否平衡
2 void assertTrue(boolean expected, boolean actual) 檢查條件為真
3 void assertFalse(boolean condition) 檢查條件為假
4 void assertNotNull(Object object) 檢查物件不為空
5 void assertNull(Object object) 檢查物件為空
6 void assertSame(boolean condition) 檢查兩個相關物件是否指向同一個物件
7 void assertNotSame(boolean condition) 檢查兩個相關物件是否不指向同一個物件
8 void assertArrayEquals(expectedArray, resultArray) 檢查兩個陣列是否相等

上表列舉了常用的八種斷言,事實上我們在實際使用中並不需要自己編寫這些基礎的斷言,java的程式設計框架JUnit已經集成了這些斷言和一系列單元測試相關的函式。
JUnit 是一個 Java 程式語言的單元測試框架(迴歸測試框架)。JUnit 在測試驅動的開發方面有很重要的發展,是起源於 JUnit 的一個統稱為 xUnit 的單元測試框架之一。JUnit 促進了“先測試後編碼”的理念,強調建立測試資料的一段程式碼,可以先測試,然後再應用。這個方法就好比“測試一點,編碼一點,測試一點,編碼一點……”,增加了程式設計師的產量和程式的穩定性,可以減少程式設計師的壓力和花費在排錯上的時間2

第一個單元測試程式

我們將從這樣一個簡單的例子開始:查詢一個list中的最大值。這次我們不妨用一種先想測試,再寫程式碼的程式設計思想。
1,首先,這個程式要保證一個list,如{7,8,9},可以把9輸出出來
2,然後呢,上面給了一個順序的list,不失一般性,這個程式不應該是順序敏感的,所以{7,9,8},{9,8,7}等也要輸出9
3,我發現,上面的list都是正數,那麼負數和0呢?{-7,-8,-9}應該返回-7吧。
4,等等,如果list裡面有重複項呢?{7,9,8,9}應該只返回一個9就可以了
5,那麼,如果list裡面只有一個元素呢?{9}當然是返回9了
6,考慮完了?空值呢?如果list為空,{ }要怎麼處理呢?
總結:1+2+5->注意上下界;3->max的初始值應為一個數型最小值;4->記錄且return一個max;6->拋個異常
程式碼如下:

//Largest.java
public class Largest {
    public static int largest (int[] list) {
        int max=Integer.MIN_VALUE;
        if(list.length == 0) {
            throw new RuntimeException("Empty list");
        }
        for(int i=0;i<list.length;i++) {
            if(list[i]>max) {
                max = list[i];
            }
        }
        return max; 
    }
}

事實上上述程式碼的測試程式碼就是考慮了最開始那6個測試例子,加以實現:

//LargestTest.java
import junit.framework.*;
public class LargestTest extends TestCase {
    public LargestTest(String name) {
        super(name);
    }
    //test numbers
    public void testNums() {
        //general test
        assertEquals(9, Largest.largest(new int[] {7,8,9}));
        //order test
        assertEquals(9, Largest.largest(new int[] {7,9,8}));
        assertEquals(9, Largest.largest(new int[] {9,8,7}));
        //repetition test
        assertEquals(9, Largest.largest(new int[] {7,9,8,9}));
        //single test
        assertEquals(9, Largest.largest(new int[] {9}));
        //negative test
        assertEquals(-7, Largest.largest(new int[] {-7,-8,-9}));
    }
    //test empty
    public void testisEmpty() {
        try {
            Largest.largest(new int[] {});
            fail("An exception should been thrown!");
        }catch(RuntimeException e) {
            assertTrue(true);
        }
    }
}

注:1,如果import junit.framework.*;報錯是因為沒有匯入junit的jar包。可以Eclipse中:右鍵工程名–>屬性–>Java Build Path–>Libraries–>Add Library,選中JUnit匯入,版本隨意。
2,相關程式碼可以直接在我的Github下載,歡迎star。

做一個簡單的總結,測試程式碼必須要做以下幾件事:
1,準備測試所需要的各種條件(建立所有必須的物件,分配必要的資源等等)
2,呼叫要測試的方法
3,驗證被測試方法的行為和期望是否一致
4,完成後清理各種資源(第三章講)

JUnit 測試骨架

下面是一段簡單的測試程式碼,它展示了讓一段斷言方法所需要的骨架的最小要求

import junit.framework.*;
public class FrameTest extends TestCase {
    public FrameTest(String name) {
        super(name);
    }
    public void testFrame(){
        ...
    }
}

儘管上面的程式碼非常簡潔,還是要簡單解釋下每一部分
1,首先,第一行的import宣告引入了必需的JUnit類。
2,接下來在第2行定義了一個類:每個包含測試的類都必須如上所示那樣有TestCase繼承而來。基類TestCase提供了我們所需的大部分單元測試功能,包括所有在前面講述過的斷言方法。
3,其中,基類需要一個以String為引數的建構函式,因而我們必須呼叫super來傳遞這麼一個名字。我們不知道這個名字此時是什麼,因而我們就僅僅讓我們自己的建構函式接受String引數並把這個引數在第4行傳遞上去。
4,最後,測試類包含了名為test的方法。在上面這個例子中,我們在第6行寫了一個名為testFrame的方法。而所有以test開頭的方法都會被JUnit自動執行。當然,還可以通過定義suite方法制定特殊的函式來執行(第三章講)。

在下一章單元測試整理(三)——JUnit 測試組成和註釋將進一步展示更完整的JUnit骨架,介紹一些複雜一點的輔助測試方法。