Java高級特性 第11節 JUnit 3.x和JUnit 4.x測試框架

1、軟件測試

  1.軟件測試的概念及分類java

  軟件測試是使用人工或者自動手段來運行或測試某個系統的過程,其目的在於檢驗它是否知足規定的需求或弄清預期結果與實際結果之間的差異。它是幫助識別開發完成(中間或最終的版本)的計算機軟件(總體或部分)的正確度 、徹底度和質量的軟件過程。數據庫

  軟件測試過程:設計模式

  

  2.軟件測試的分類數組

  按是否關心軟件內部結構和具體實現角度來分:安全

  • 黑盒測試(Black-box Testing)

  黑盒測試也稱功能測試,測試中把被測的軟件當成一個黑盒子,不關心盒子的內部結構是什麼,只關心軟件的輸入數據與輸出數據。數據結構

  •  白盒測試(White-box Testing)

  白盒測試又稱結構測試、透明盒測試、邏輯驅動測試或基於代碼的測試。白盒指的打開盒子,去研究裏面的源代碼和程序結果。框架

  • 灰盒測試(Gray-Box Testing)

  灰盒測試,是介於白盒測試與黑盒測試之間的一種測試,灰盒測試多用於集成測試階段,不只關注輸出、輸入的正確性,同時也關注程序內部的狀況。函數

  從軟件開發過程的階段,可分爲:oop

  • 單元測試(Unit Testing)

  單元測試是對軟件組成單元進行測試。其目的是檢驗軟件基本組成單位的正確性。測試的對象是軟件設計的最小單位:模塊。Findyou又稱爲模塊測試性能

    • 測試階段:編碼後

    • 測試對象:最小模塊

    • 測試人員:白盒測試工程師或開發工程師

    • 測試依據:代碼和註釋+詳細設計文檔

    • 測試方法:白盒測試

    • 測試內容:模塊接口測試、局部數據結構測試、路徑測試、錯誤處理測試、邊界測試

  •  集成測試(Integration Testing)

  集成測試也稱聯合測試、組裝測試,將程序模塊採用適當的集成策略組裝起來,對系統的接口及集成後的功能進行正確性檢測的測試工做。阿旺主要目的是檢查軟件單位之間的接口是否正確。

    • 測試階段:通常單元測試以後進行

    • 測試對象:模塊間的接口

    • 測試人員:白盒測試工程師或開發工程師

    • 測試依據:單元測試的模塊+概要設計文檔

    • 測試方法:黑盒測試與白盒測試相結合

    • 測試內容:模塊之間數據傳輸、模塊之間功能衝突、模塊組裝功能正確性、全局數據結構、單模塊缺陷對系統的影響

  •  系統測試(System Testing)

  將軟件系統當作是一個系統的測試。包括對功能、性能以及軟件所運行的軟硬件環境進行測試。時間大部分在系統測試執行階段

    • 測試階段:集成測試經過以後

    • 測試對象:整個系統(軟、硬件)

    • 測試人員:黑盒測試工程師

    • 測試依據:需求規格說明文檔

    • 測試方法:黑盒測試

    • 測試內容:功能、界面、可靠性、易用性、性能、兼容性、安全性等

  •  驗收測試(Acceptance Testing)

  驗收測試是部署軟件以前的最後一個測試操做。它是技術測試的最後一個階段,也稱爲交付測試。阿旺總結驗收測試的目的是確保軟件準備就緒,按照項目合同、任務書、雙方約定的驗收依據文檔,向軟件購買都展現該軟件系統知足原始需求。

    • 測試階段:系統測試經過以後

    • 測試對象:整個系統(包括軟硬件)。

    • 測試人員:主要是最終用戶或者需求方。

    • 測試依據:用戶需求、驗收標準

    • 測試方法:黑盒測試

    • 測試內容:同系統測試(功能...各種文檔等)

2、JUnit測試框架

  1.JUnit測試框架概述

   Junit是一套框架(用於JAVA語言),由 Erich Gamma 和 Kent Beck 編寫的一個迴歸測試框架(regression testing framework),即用於白盒測試。

  1)使用JUnit的好處: 

  • 可使測試代碼與產品代碼分開。 
  • 針對某一個類的測試代碼經過較少的改動即可以應用於另外一個類的測試。 
  •  易於集成到測試人員的構建過程當中,JUnit和Ant的結合能夠實施增量開發。 
  • JUnit是公開源代碼的,能夠進行二次開發。 
  • 能夠方便地對JUnit進行擴展。

  2)JUnit的特徵 

  • 使用斷言方法判斷指望值和實際值差別,返回Boolean值。 
  • 測試驅動設備使用共同的初始化變量或者實例。 
  • 測試包結構便於組織和集成運行。 
  • 支持圖型交互模式和文本交互模式。

  2.JUnit測試框架包介紹

   JUnit測試框架包含了JUnit測試類所須要的全部基類,實際上這個包也是整個JUnit的框架基礎。

  • Assert靜態類:一系列斷言方法的集合   

  Assert包含了一組靜態的測試方法,用於指望值和實際值邏輯比對是否正確(這個過程稱爲斷言),如測試失敗,Assert類就會拋出一AssertionFailedError異常,JUnit測試框架將這種錯誤納入Failes並加以記錄,同時標誌爲未經過測試。若是該類方法中指定一個String類型的傳參則該參數將被作爲AssertionFailedError異常的標識信息,告訴測試人員改異常的詳細信息。 
  JUnit 提供了6大類31組斷言方法,包括基礎斷言、數字斷言、字符斷言、布爾斷言、對象斷言。

  其中assertEquals(Object expcted,Object actual)內部邏輯判斷使用equals()方法,這代表斷言兩個實例的內部哈希值是否相等時,最好使用該方法對相應類實例的值進行比較。 
  而assertSame(Object expected,Object actual)內部邏輯判斷使用了Java運算符「==」,這代表該斷言判斷兩個實例是否來自於同一個引用(Reference),最好使用該方法對不一樣類的實例的值進行比對。 
  asserEquals(String message,String expected,String actual)該方法對兩個字符串進行邏輯比對,若是不匹配則顯示着兩個字符串有差別的地方。 
  ComparisonFailure類提供兩個字符串的比對,不匹配則給出詳細的差別字符。 

  • Test接口:運行測試和收集測試結果 

  Test接口使用了Composite設計模式,是單獨測試用例(TestCase),聚合測試模式(TestSuite)及測試擴展(TestDecorator)的共同接口。 它的public int countTestCases()方法,用來統計測試時有多少個TestCase。另一個方法就是public void run( TestResult ),TestResult是實例接受測試結果, run方法執行本次測試。 

  • TestCase抽象類:核心部分,定義測試中固定方法 

  TestCase是Test接口的抽象實現,(不能被實例化,只能被繼承)其構造函數TestCase(string name)根據輸入的測試名稱name建立一個測試實例。因爲每個TestCase在建立時都要有一個名稱,若測試失敗了,即可識別出是哪一個測試失敗。 
  TestCase類中包含的setUp()、tearDown()方法。 
     setUp()方法集中初始化測試所需的全部變量和實例,而且在依次調用測試類中的每一個測試方法以前再次執行setUp()方法。 
  tearDown()方法則是在每一個測試方法以後,釋放測試程序方法中引用的變量和實例。
  開發人員編寫測試用例時,只需繼承TestCase,來完成run方法便可,而後JUnit得到測試用例,執行它的run方法,把測試結果記錄在TestResult之中。 

  • TestSuite測試包類:多個測試的組合 

  TestSuite類負責組裝多個Test Cases。待測得類中可能包括了對被測類的多個測試,而TestSuit負責收集這些測試,使咱們能夠在一個測試中,完成所有的對被測類的多個測試。TestSuite類實現了Test接口,且能夠包含其它的TestSuites。它能夠處理加入Test時的全部拋出的異常。
  TestSuite處理測試用例有6個規約(不然會被拒絕執行測試) :
  1)測試用例必須是公有類(Public) 
  2)用例必須繼承與TestCase類 
  3)測試用例的測試方法必須是公有的( Public ) 
  4) 測試用例的測試方法必須被聲明爲Void 
  5)測試用例中測試方法的前置名詞必須是test 
  6)測試用例中測試方法誤任何傳遞參數 

  • TestRunner運行包類

  TestRunner運行包類是運行測試代碼的運行器。

  • TestResult結果類

  TestResult結果類集合了任意測試累加結果,經過TestResult實例傳遞給每一個測試的Run()方法。TestResult在執行TestCase是若是失敗會異常拋出。 

  • 其它類與接口 

  TestListener接口是個事件監聽規約,可供TestRunner類使用。它通知listener的對象相關事件,方法包括測試開始startTest(Test test),測試結束endTest(Test test),錯誤,增長異常addError(Test test,Throwable t)和增長失敗addFailure(Test test,AssertionFailedError t)。 
  TestFailure失敗類是個「失敗」情況的收集類,解釋每次測試執行過程當中出現的異常狀況。其toString()方法返回「失敗」情況的簡要描述

  3.JUnit 3.x測試框架

  • JUnit 3.x 測試框架概述

   JUnit是一個很是強大的單元測試包,JUnit 3.x中會自動執行test開頭的方法,這是依賴反射執行的

  • 使用JUnit 3.x 進行單元測試

   搭建JUnit測試框架,必須瞭解下面那幾個方法的做用:

   1)testXxx():JUnit 3.x自動調用並執行的方法,必須是public而且不能帶有參數,必須以test開頭,返回值爲void

   2)setUp():初始化,準備測試環境。

   3)tearDown():釋放資源

   它們的調用順序是:setUp()--->testXxx()---->tearDown()

   使用JUnit 3.x進行單元測試通常按照下列步驟進行:

   1)在Java工程中導入所須要的JUnit測試jar包,選中setUp()方法和tearDown()方法;

   2)在Java工程中選中要測試的方法並完成測試類的方法編寫;

   3)執行程序,紅色表明失敗,綠色表明成功。

package cn.yu; 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)); // }
}
package cn.yu; import junit.framework.Assert; import junit.framework.TestCase; public class CalculatorTest extends TestCase { Calculator calculator; public void setUp() throws Exception { calculator = new Calculator(); } public void tearDown() throws Exception { super.tearDown(); } /** * * Method: add(int x, int y) * */
    public void testAdd() throws Exception { Assert.assertEquals(calculator.add(8,2),10); } /** * * Method: sub(int x, int y) * */
    public void testSub() throws Exception { Assert.assertEquals(calculator.sub(8,2),7); //預期值和實際值不符,這裏在測試時會報錯 } /** * * Method: mul(int x, int y) * */
    public void testMul() throws Exception { Assert.assertEquals(calculator.mul(8,2),16); } /** * * Method: div(int x, int y) * */
    public void testDiv() throws Exception { Assert.assertEquals(calculator.div(8,2),4); } /** * * Method: div2(int x, int y) * */
    public void testDiv2() throws Exception { } /** * * Method: loop(int x, int y) * */
    public void testLoop() throws Exception { } /** * * Method: unCompleted(int x, int y) * */
    public void testUnCompleted() throws Exception { } }

  

  4.JUnit 4.x 測試框架

  • JUnit 4.x 測試框架概述

   JUnit 4.x對JUnit框架進行了顛覆性的改變,主要利用了JDK5.0中的新特性Annotation的特色來簡化測試用例的編寫。

  要前面注意到,使用JUnit 3.x時有一些比較霸道的地方,表如今:

  1. 單元測試類必須繼承自TestCase。

  2. 要測試的方法必須以test開頭。

  採用Annotation的JUnit 4.x已經不會霸道的要求必須繼承自TestCase了,並且測試方法也沒必要以test開頭了,只要以@Test元數據來描述便可。 JUnit 4.x中經常使用的註解以下:  

  • @Before: 

  使用了該元數據的方法在每一個測試方法執行以前都要執行一次。

  • @After: 

  使用了該元數據的方法在每一個測試方法執行以後要執行一次。

  注意:@Before和@After標示的方法只能各有一個。這個至關於取代了JUnit之前版本中的setUp()(執行初始化)和tearDown()(釋放資源)方法,固然還能夠繼續叫這個名字,也能夠隨意定義名字。

  • @BeforeClass:

  全部測試方法執行前執行一次,在測試類尚未實例化就已經被加載,因此用static修飾。

  • @AfterClass:

  全部測試方法執行完執行一次,在測試類尚未實例化就已經被加載,因此用static修飾。

  • @Test(expected=*.class) 

  在JUnit4.0以前,對錯誤的測試,咱們只能經過fail來產生一個錯誤,並在try塊裏面assertTrue(true)來測試。如今,經過@Test元數據中的expected屬性。expected屬性的值是一個異常的類型

  • @Test(timeout=xxx): 

  該元數據傳入了一個時間(毫秒)給測試方法, 若是測試方法在制定的時間以內沒有運行完,則測試也失敗。

  • @ignore: 

  該元數據標記的測試方法在測試中會被忽略。當測試的方法尚未實現,或者測試的方法已通過時,或者在某種條件下才能測試該方法(好比須要一個數據庫聯接,而在本地測試的時候,數據庫並無鏈接),那麼使用該標籤來標示這個方法。同時,你能夠爲該標籤傳遞一個String的參數,來代表爲何會忽略這個測試方法。好比:@lgnore(「該方法尚未實現」),在執行的時候,僅會報告該方法沒有實現,而不會執行並報錯。

  • 使用JUnit 4.x 進行單元測試
package cn.yu; 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)); // }
}
package cn.yu; import org.junit.*; public class CalculatorTest4 { @Before public void setUp() throws Exception { System.out.println("@Before");//測試@Before
 } @After public void end() throws Exception { System.out.println("@After");//測試@@After
 } @BeforeClass public static void init() throws Exception { System.out.println("@BeforeClass");//測試@BeforeClass
 } @AfterClass public static void disstroy() throws Exception { System.out.println("@AfterClass");//測試@AfterClass
 } @Test public void testAdd() { System.out.println("@Test testAdd");//測試@Test
 } @Test public void testSub() { System.out.println("@Test testSub");//測試@Test
 } @Ignore public void testDiv() { System.out.println("@Ignore ");//測試@Ignore
 } @Ignore public void testDiv2() { System.out.println("@Ignore ");//測試@Ignore
 } @Ignore public void testLoop() { System.out.println("@Ignore ");//測試@Ignore
 } public void testUnCompleted() { System.out.println("@Ignore ");//測試未標註
 } }

  

 3、JUnit測試套件

  若是在測試類不端增長的狀況下,如何運行全部的單元測試代碼類?一個個測試類的執行嗎?顯然繁瑣且費勁。
       將要運行的測試類集成在咱們的測試套件中,好比一個系統功能對應一個測試套件,一個測試套件中包含多個測試類,每次測試系統功能時,只要執行一次測試套件就能夠了。

   1. 測試類及測試套件代碼
  • 新建3個測試任務類:
package jtzen9.util; import org.junit.Test; public class TaskTest1 { @Test public void test() { System.out.println("this is TaskTest1..."); } } /***************************************/ package jtzen9.util; import org.junit.Test; public class TaskTest2 { @Test public void test() { System.out.println("this is TaskTest2..."); } } /*********************************************/ package jtzen9.util; import org.junit.Test; public class TaskTest3 { @Test public void test() { System.out.println("this is TaskTest3..."); } }
  當類被@RunWith註解修飾,或者類繼承了一個被該註解修飾的類,JUnit將會使用這個註解所指明的運行器(runner)來運行測試,而不是JUnit默認的運行器。
package jtzen9.util; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({TaskTest1.class,TaskTest2.class,TaskTest3.class}) public class SuiteTest { /* * 1.測試套件就是組織測試類一塊兒運行的 * * 寫一個做爲測試套件的入口類,這個類裏不包含其餘的方法 * 更改測試運行器Suite.class * 將要測試的類做爲數組傳入到Suite.SuiteClasses({}) */ }
  運行結果:
  
  • 說明
        ①使用@RunWith註解,修改測試運行器。例如@RunWith(Suite.class),這個類就成爲測試套件的入口類。
        ②@Suite.SuiteClasses()中放入測試套件的測試類,以數組的形式{class1,class2,......}做爲參數。
 
   2. JUnit參數化設置
        若是測試代碼大同小異,代碼結構都是相同的,不一樣的只是測試的數據和預期值,那麼有沒有更好的辦法將相同的代碼結構提取出來,提升代碼的重用度呢?
        解決:進行參數化測試。
        步驟:
  ①要進行參數化測試,須要在類上面指定以下的運行器:@RunWith (Parameterized.class)
  ②而後,在提供數據的方法上加上一個@Parameters註解,這個方法必須是靜態static的,而且返回一個集合Collection。
  ③在測試類的構造方法中爲各個參數賦值,(構造方法是由JUnit調用的),最後編寫測試類,它會根據參數的組數來運行測試屢次。
  • 代碼: 

package jtzen9.util; import static org.junit.Assert.*; 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)   //1.更改默認的測試運行器爲RunWith(Parameterized.class)
public class ParameterTest { //2.聲明變量存放預期值和測試數據
    int expected =0; int input1 = 0; int input2 = 0; //3.聲明一個返回值 爲Collection的公共靜態方法,並使用@Parameters進行修飾
 @Parameters public static Collection<Object[]><object> data() { return Arrays.asList(new Object[][]{ {3,1,2}, {4,2,2} }) ; } //4.爲測試類聲明一個帶有參數的公共構造函數,並在其中爲之聲明變量賦值
    public ParameterTest(int expected,int input1,int input2) { this.expected = expected; this.input1 = input1; this.input2 = input2; } //5.運行測試方法,便可完成對多組數據的測試
 @Test public void testAdd() { assertEquals(expected, new Calculate().add(input1, input2)); } }
  • 運行結果
  
相關文章
相關標籤/搜索