1. 程式人生 > >JUnit單元測試教程(翻譯自Java Code Geeks)

JUnit單元測試教程(翻譯自Java Code Geeks)

說明

本教程翻譯自Java Code Geeks,原文網址:https://www.javacodegeeks.com/2014/11/junit-tutorial-unit-testing.html#suite_tests。翻譯的過程中,有少部分內容是譯者新增的解釋說明和對原文章中錯誤地方的修正。水平有限,如果有錯誤的地方,希望能在我的部落格裡告訴我,謝謝。 
相關的例子不想自己寫的同學們可以去這裡下載:http://download.csdn.net/detail/rainnnbow/9603847

1. 單元測試簡介

1.1 什麼是單元測試?

一個單元可以是一個方法、一個類、一個包、 或者一個子系統。所以術語單元測試表示對程式碼中的很小的單元的測試行為,以確保它們能夠按預期工作。例如,我們給定一些輸入,測試是否是預期的輸出,或者測試一個條件是真還是假。 
這種測試行為能幫助開發者發現隱藏在程式碼後面的錯誤,並提高程式碼質量。單元測試也被用來確保程式碼能夠按照預期工作,以防止未來可能的變化。

1.2 測試覆蓋

總的來說,開發者社群對程式碼測試要達到多少比例存在不同的觀點。許多開發者認為程式碼的測試覆蓋率要達到100%。另一些則認為50%或者更低就可以了。但無論如何,你都應該為你的程式碼中複雜的以及重要的部分編寫測試。

1.3 Java中的單元測試

Java中最著名的測試框架是JUnit,本指南是專門介紹JUnit的,接下來的部分將詳細介紹該測試框架的更多細節。Java中另一個著名的測試框架是TestNG。

2. JUnit簡介

JUnit是一個開源的測試框架,它被用來編寫和執行可重複的自動化測試,保證我們的程式碼按照預期的執行。JUnit被廣泛使用:它可以在Java程式中單獨使用(使用命令列)或者結合IDE使用,如Eclipse。 
JUnit提供以下功能:

  • 斷言測試的預期結果。
  • 共享測試資料的特性。
  • 使用Test suites簡化測試用例的組織和執行。
  • 圖形化和文字化測試用例?(Graphical and textual test runners.)

JUnit可以用來測試以下內容:

  • 一個物件
  • 部分物件——一個方法或幾個相互影響的方法
  • 多個物件之間的互動

2.1 使用Eclipse實現簡單JUnit測試例子

在這一節我們將展示一個簡單的JUnit例子。首先來看一下我們要測試的類: 
Calculate.java:

package com.javacodegeeks.junit;

public class Calculate {

    public int sum(int var1, int var2) { System.out.println("Adding values: " + var1 + " + " + var2); return var1 + var2; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在上面的原始碼中,我們看到該類有一個public的方法sum(),方法接受兩個int引數,相加之後返回結果。我們將測試這個方法。因此我們需要建立另一個類,並在這個類中為測試Calculate類中的所有需要測試的方法一一編寫測試方法(在這個例子中,我們只有一個方法需要測試)。這是最普通的使用方法。當然如果一個方法非常複雜,我們可以為這個方法編寫多個測試方法。編寫測試用例的更多細節,我們將在下一節介紹。下面的CalculateTest.java類就是我們的測試類:

package com.javacodegeeks.junit;

import static org.junit.Assert.*;

import org.junit.Test;

public class CalculateTest { Calculate calculation = new Calculate(); int sum = calculation.sum(2, 5); int testSum = 7; @Test public void testSum() { System.out.println("@Test sum(): " + sum + " = " + testSum); assertEquals(sum, testSum); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

讓我們來解釋一下上面的程式碼。首先我們看到在testSum()方法上有一個@Test註解。這個註解表示這個方法可以作為一個測試用例執行。所以testSum()方法被用來測試sum()方法。我們還看到了assertEquals(sum, testSum)方法,assertEquals ([String message], object expected, object actual)方法用來接手兩個物件,並斷言這兩個物件是否相等。 
執行該測試類,Run As–>JUnit Test,程式輸出將如下所示:

這裡寫圖片描述

為了檢視JUnit測試的實際結果,Eclipse提供了JUnit視窗來顯示測試結果。在這個用例中,測試成功,JUnit視窗不會顯示任何錯誤或失敗資訊,所以我們看到了如下所示的介面:

這裡寫圖片描述

如果我們改變一下程式碼:

int testSum = 10;
  • 1

修改程式碼後兩個整數的測試不再相等,程式輸出將如下所示:

這裡寫圖片描述

同時,在JUnit視窗中會顯示測試出錯,並給出錯誤資訊:

這裡寫圖片描述

2.2 JUnit註解 
本節我們將介紹JUnit4支援的基本註解,下表是對這些索引的總結:

這裡寫圖片描述

讓我們來看一個包含了以上註解的測試類的例子: 
AnnotationsTest.java:

package com.javacodegeeks.junit;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import java.util.ArrayList; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; public class AnnotationsTest { private ArrayList testList; /** * if the method isn't static, throw following exception: * java.lang.Exception: Method onceExecutedBeforeAll() should be static */ @BeforeClass public static void onceExecutedBeforeAll() { System.out.println("@BeforeClass: onceExecutedBeforeAll"); } @Before public void executedBeforeEach() { testList = new ArrayList(); System.out.println("@Before: executedBeforeEach"); } /** * If the method isn't static, throw following exception: * java.lang.Exception: Method onceExecutedAfterAll() should be static */ @AfterClass public static void onceExecutedAfterAll() { System.out.println("@AfterClass: onceExecutedAfterAll"); } @After public void executedAfterEach() { testList.clear(); System.out.println("@After: executedAfterEach"); } @Test public void EmptyCollection() { assertTrue(testList.isEmpty()); System.out.println("@Test: EmptyArrayList"); } @Test public void OneItemCollection() { testList.add("oneItem"); assertEquals(1, testList.size()); System.out.println("@Test: OneItemArrayList"); } @Ignore public void executionIgnored() { System.out.println("@Ignore: This execution is ignored"); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

執行以上測試,控制檯輸出如下所示:

這裡寫圖片描述

JUnit視窗顯示如下資訊:

這裡寫圖片描述

2.3 Junit斷言 
本節我們將展示一些斷言方法。所有這些方法由Assert類提供,Assert類繼承自java.lang.Object。這些斷言能幫助你編寫測試用例,監測到失敗的測試。下表是對一些最常用的斷言方法的詳細介紹:

這裡寫圖片描述

讓我們看一個上述斷言的例子: 
AssertionsTest.java

package com.javacodegeeks.junit;

import static org.junit.Assert.*;

import org.junit.Test;

public class AssertionsTest { @Test public void test() { String obj1 = "junit"; String obj2 = "junit"; String obj3 = "test"; String obj4 = "test"; String obj5 = null; int var1 = 1; int var2 = 2; int[] arithmetic1 = {1,2,3}; int[] arithmetic2 = {1,2,3}; assertEquals(obj1, obj2); assertSame(obj3, obj4); assertNotSame(obj2, obj4); assertNotNull(obj1); assertNull(obj5); assertTrue(var1 != var2); assertFalse(var1 == var2); assertArrayEquals(arithmetic1, arithmetic2); } } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

從以上類中我們可以看到斷言方法是怎樣工作的。

  • assertEquals():如果兩個物件相等,方法正常返回;否則在JUnit視窗中將顯示錯誤,該測試將被中斷。
  • assertSame()與assertNotSame():測試兩個物件的引用是否指向了同一個物件。
  • assertNull()與assertNotNull():測試一個變數是否是null或非null。
  • assertTrue()與assertFalse():測試一個條件或變數時true或false.。
  • assertArrayEquals():比較兩個陣列是否相等。如果相等,方法正常返回;否則在JUnit視窗中將顯示錯誤,該測試將被中斷。

3. 使用Eclipse實現完整的JUnit例子

本節我們將展示一個使用JUnit的完整例子。我們將講解怎樣建立和執行測試用例,以及怎樣使用JUnit的註解和斷言。

3.1 建立工程

建立一個名為JUnitGuide的java工程。建立一個包com.javacodegeeks.junit用來放被測試的類。對於測試類,一個好的實踐是建立一個專用於測試的原始碼包。這樣被測試類和測試類就被分在了不同的原始碼包中。建立test包:右鍵單擊你的工程, New–>Source Folder,命名原始碼包為test。 
這裡寫圖片描述 
提示:建立原始碼包的另一種方式:右鍵單擊你的工程PropertiesJava Build Path,選擇Source 項,然後Add FolderCreate New Folder。輸入原始碼包名,點選finish。 
你能看到在工程裡有兩個原始碼包: 
這裡寫圖片描述 
你可以在test原始碼包中也建立一個名稱為com.javacodegeeks.junit的包,這樣你的測試類就不會被放在預設的包中。

3.2 建立要被測試的Java類

右擊src原始碼包,建立一個名為FirstDayAtScholl.java的類。該類的public方法將被測試。 
FirstDayAtSchool.java 
這裡寫圖片描述

3.3 建立並執行JUnit測試用例

為已經存在的類FirstDayAtSchool.java建立測試用例:選中FirstDayAtSchool.java類,右擊選擇NewJUnit Test Case。改變原始碼包,將測試用例類放在test原始碼包中。確保選擇“JUnit 4 test”選項。 
這裡寫圖片描述

點選“Finish”。如果你的專案classpath下還沒有包含JUnit4,將會彈出如下所示的資訊讓你將JUnit4 Library新增進你的工程中。 
這裡寫圖片描述 
(譯者注:也可以直接在test原始碼包中像建立普通類一樣建立一個測試用例類。只要該類中的方法用@Test註解標註上,就能引入JUnit框架,併成為測試用例。)

以下就是我們建立的名為FirstDayAtSchoolTest.java的測試用例。 
FirstDayAtSchoolTest.java 
這裡寫圖片描述

右鍵Run AsJUnit Test,執行測試用例。 
程式輸出如下所示: 
這裡寫圖片描述 
JUnit視窗顯示如下: 
這裡寫圖片描述 
此時JUnit視窗沒有任何錯誤或失敗。如果我們改變其中一個數組,讓它包含比預期更多的元素: 
這裡寫圖片描述 
再次執行測試類,JUnit視窗將顯示有一個失敗的測試用例: 
這裡寫圖片描述 
或者,我們再次改變陣列,讓它包含與預期不同的元素: 
這裡寫圖片描述 
再次執行測試類,JUnit視窗將再次顯示一個測試用例fail: 
這裡寫圖片描述

3.4 使用@Ignore註解

在上面的例子中,讓我們看看怎樣使用@Ignore註解。在測試類FirstDayAtSchoolTest.java的testAddPencils()方法上標註@Ignore註解。通過這種方式,我們將該測試方法忽略掉,測試時不會執行。 
這裡寫圖片描述 
執行測試,輸出如下所示:@Ignore註解的測試方法沒有執行。 
這裡寫圖片描述 
JUnit視窗顯示如下: 
這裡寫圖片描述

現在,我們去掉testAddPencils()方法上的@Ignore註解,將@Ignore註解標註在FirstDayAtSchoolTest.java類上。 
這裡寫圖片描述 
執行該測試類發現:整個測試用例將不會執行,所以控制檯上沒有任何輸出。JUnit視窗顯示如下資訊: 
這裡寫圖片描述

3.5 建立測試套件(suite tests)

本節,我們將展示如何建立測試套件。測試套件是來自於不同類的多個測試用例的集合。使用@Runwith和@Suite註解可以讓這些測試類一起執行。當你有許多測試類並且你希望這些測試類一起執行時,這是非常有用的。 
在上一節類的基礎上,我們可以建立兩個測試類。一個測試prepareMyBag()方法,另一個測試addPencils()方法。兩個測試類如下所示:

PrepareMyBagTest.java. 
這裡寫圖片描述

AddPencilsTest.java. 
這裡寫圖片描述

我們來建立一個測試套件類來執行以上兩個測試類。測試套件類SuiteTest.java如下所示: 
SuiteTest.java. 
這裡寫圖片描述 
使用@Suite.SuiteClasses註解來定義哪些測試類將被包含進測試套件一起執行。 
執行該測試套件,這兩個測試類將按照在@Suite.SuiteClasses註解中宣告的順序執行。 
執行完畢後JUnit視窗顯示資訊如下: 
這裡寫圖片描述

3.6 建立引數化測試(parameterized tests)

本節我們將展示如何建立引數化的測試。我們將使用前面的2.1節中的類作為被測試類。 
什麼時候一個測試類才能被認為是一個引數化的測試類呢?當測試類滿足下列條件的時候:

 該類使用@RunWith(Parameterized.class)標註; 
@RunWith註解告訴JUnit使用該註解標明的執行器執行測試用例,而不是使用JUnit內建的預設執行器。Parameterized是JUnit中的一個執行器,該執行器將使用一組不同的輸入來多次執行一個相同的測試用例。 
 該類有唯一的建構函式來建立測試資料; 
 該類有一個static方法用來產生並返回測試資料,該方法使用@Parameters註解標註; 
 該類有一個使用@Test標註的測試用例(譯者注:廢話嗎這不是)。 
我們建立了一個新的測試類CalculateTest.java。該類遵循以上的規則。該類原始碼如下: 
這裡寫圖片描述

上如程式碼滿足以上的所有規則。addedNumbers方法使用@Parameters標註,返回陣列的集合。每一個數組包含一次測試執行的I/O資料。陣列中元素的個數必須和建構函式中引數的個數一致。在該例子中,一個數組包含三個元素,兩個代表要被相加的數,一個代表結果。

執行該測試用例,控制檯輸出如下所示: 
這裡寫圖片描述 
JUnit視窗輸出資訊如下: 
這裡寫圖片描述

從上述資訊可以看出,該測試用例執行了4次,這正是使用@Parameters註解標註的方法中輸入的測試資料的個數。

3.7 規則(Rules)

本節我們將介紹JUnit4的一個新特性,叫做規則(Rules)。Rules允許我們靈活的增加或重新定義每一個測試方法的行為。為達到這個目的,使用@Rule註解標註測試類中public的域(fields)。這些域必須是MethodRule型別的,他們標明瞭一個測試方法的執行或結果報告作出了哪些改變。一個測試方法可以應用多個MethodRule。MethodRule介面有許多實現,如ErrorCollector,該規則允許測試用例在發現第一次錯誤是可以繼續執行;ExpectedException,該規則允許我們明確預期異常的型別及異常資訊;TestName,該規則允許我們在測試方法中獲取到當前測試方法的名稱;等等其他規則。除了JUnit已經定義的規則,開發者也可以自定義規則並在測試用例中使用。

下面,我們展示一個使用已經存在的TestName規則的例子。當一個測試類開始執行時TestName被呼叫。 
NameRuleTest.java 
這裡寫圖片描述

如上所示:@Rule註解標註了一個public的域name,該域是MethodRule型別的。確切的說,該類是MethodRule介面的實現類MethodRule型別。然後我們就可以使用name在我們的測試方法中獲取當前正在執行的測試方法的名字了。

(譯者注:關於Rules的詳細說明,請檢視JUnit4官方文件:https://github.com/junit-team/junit4/wiki/Rules。執行裡面的例子,有疑問就修改例子測試,一目瞭然。Rules還是有不少用處的。)

3.8 策略(Categories)

JUnit的另一個新特性是策略,這允許你將特定的測試放在一起形成一個分組(group),並可以包含或排除某些分組。例如你可以將執行慢的測試用例與執行快的測試用例分開。JUnit提供了@Category註解為一個測試用例標明策略。以下是使用該新特性的例子。該特性從JUnit4.8開始支援。 
這裡寫圖片描述 
首先,定義兩個策略,FastTests和SlowTests。一個策略可以是一個類或者一個藉口。

這裡寫圖片描述 
以上的程式碼中,我們在A類的b()方法上使用了@Category註解,標註該方法在SlowTests策略中。所以,@Category註解不僅可以標註整個類,也可以標註某一個測試方法。 
這裡寫圖片描述

以上的程式碼中,我們看到整個B類使用了@Category標註。在類上標註後,該類的所有測試方法都會自動包含進@Category中宣告的策略中。我們也可以看到,一個類或者一個測試方法可以屬於多個策略。 
這裡寫圖片描述

以上程式碼中,我們看到有一個名為SlowTestSuite_1的測試套件。一般情況下,我們認為策略就是一種測試套件。在該套件中,我們使用@IncludeCategory註解標明哪些策略將會被執行。在該例中,屬於SlowTests策略的所有方法將會被執行。因此,A類中的b()方法和B類中的c方法將被執行,他們都屬於SlowTests策略。

這裡寫圖片描述 
以上程式碼中,我們對測試套件做了一些修改,我們添加了一個新的註解@ExcludeCategory,該註解表明那些策略將被排除。在本例中,只有A類的b()方法會執行,因為他是唯一隻屬於SlowTests策略,並不屬於FastTests策略的測試方法。

通過以上兩個例子我們看到,A類的a()方法從未執行,因為它不屬於任何策略。

5. 命令列執行JUnit測試

通過使用org.junit.runner.JUnitCore 類,可以在Eclipse之外執行測試用例。該類提供了runClasses()方法允許執行一個或多個測試類。runClasses()方法的返回型別是一個org.junit.runner.Result物件。該物件可以蒐集所有測試的資訊。如果有失敗的測試用例,你可以使用org.junit.runner.notification.Failure物件來獲取失敗的測試用例的描述。 
下面的流程展示瞭如何在Eclipse之外執行你的測試。 
建立一個新類JunitRunner.java如下所示: 
JunitRunner.java 
這裡寫圖片描述 
我們選擇執行名為AssertionsTest的測試類。 
 開啟DOS視窗,並定位到JunitRunner.java所在的目錄(譯者注:本人的目錄是**\JUnitGuide\test\com\javacodegeeks\junit)。 
 編譯測試類和Runner類。 
javac -classpath “D:\learnTech\test\junit-4.11.jar”;”D:\learnTech\test\hamcrest-core-1.3.0.jar”; AssertionsTest.java JunitRunner.java 
這裡寫圖片描述
和在Eclipse中一樣,我們需要將JUnit的jar包包含進classpath中。執行完上述命令。在Junitrunner.java所在的目錄下就生成了AssertionsTest.class 和JunitRunner.class兩個class檔案。如下所示: 
這裡寫圖片描述 
 在DOS視窗中,退回到test原始碼目錄(譯者注:本人的目錄是**\JUnitGuide\test)。 
 執行JunitRunner類。 
java -classpath “D:\learnTech\test\junit-4.11.jar”;”D:\learnTech\test\hamcrest-core-1.3.0.jar”; JunitRunner 
這裡寫圖片描述
輸出結果為: 
這裡寫圖片描述

 

from: https://blog.csdn.net/Rainnnbow/article/details/52217058