單元測試整理(二)——斷言篇,首個單元測試程式
引子
單元測試主要是用來驗證所測程式碼是否和程式設計師的期望一致,如下所示,在實際操作中,我們可以編寫一個函式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骨架,介紹一些複雜一點的輔助測試方法。