Java JUnit 單元測試小結

測試類型

單元測試(Unit test)

單元測試關注單一的類. 它們存在的目的是檢查這個類中的代碼是否按照指望正確運行.數據庫

集成測試(Integration test)

顧名思義, 集成測試是檢查開發的模塊和其餘模塊整合時是否正常工做.
雖然集成測試的代碼影響範圍比單元測試要廣, 可是集成測試和單元測試同樣, 也是針對於開發者而言的.segmentfault

端到端測試(End-to-End test)

端到端測試是將整個系統做爲一個總體, 而後從用戶的角度進行測試的.
端到端測試的目的是測試系統在實際使用的是否正常的, 所以一般來講是不須要測試替身的(Test Double)服務器

單元測試基本概念

什麼是單元測試

單元測試的目的: 測試當前所寫的代碼是不是正確的, 例如輸入一組數據, 會輸出指望的數據; 輸入錯誤數據, 會產生錯誤異常等.
在單元測試中, 咱們須要保證被測系統是獨立的(SUT 沒有任何的 DOC), 即當被測系統經過測試時, 那麼它在任何環境下都是可以正常工做的. 編寫單元測試時, 僅僅須要關注單個類就能夠了. 而不須要關注例如數據庫服務, Web 服務等組件.函數

被測系統

被測系統(System under test, SUT)表示正在被測試的系統, 目的是測試系統可否正確操做.
根據測試類型的不一樣, SUT 指代的內容也不一樣, 例如 SUT 能夠是一個類甚至是一整個系統.單元測試

測試依賴組件(DOC)

被測系統所依賴的組件, 例如進程 UserService 的單元測試時, UserService 會依賴 UserDao, 所以 UserDao 就是 DOC.測試

測試替身(Test Double)

一個實際的系統會依賴多個外部對象, 可是在進行單元測試時, 咱們會用一些功能較爲簡單的而且其行爲和實際對象相似的假對象來做爲 SUT 的依賴對象, 以此來下降單元測試的複雜性和可實現性. 在這裏, 這些假對象就被稱爲 測試替身(Test Double).
測試替身有以下 5 種類型:ui

  • Test stub, 爲 SUT 提供數據的假對象.
    咱們舉一個例子來展現什麼是 Test stub.spa

假設咱們的一個模塊須要從 HTTP 接口中獲取商品價格數據, 這個獲取數據的接口被封裝爲 getPrice 方法. 在對這個模塊進行測試時, 咱們顯然不太可能專門開一個 HTTP 服務器來提供此接口, 而是提供一個帶有 getPrice 方法的假對象, 從這個假對象中獲取數據.
在這個例子中, 提供數據的假對象就叫作 Test stub.code

  • Fake object
    實現了簡單功能的一個假對象. Fake object 和 Test stub 的主要區別就是 Test stub 側重於用於提供數據的假對象, 而 Fake object 沒有這層含義.對象

使用 Fake object 的最主要的緣由就是在測試時某些組件不可用或運行速度太慢, 於是使用 Fake object 來代替它們.

  • Mock object
    用於模擬實際的對象, 而且可以校驗對這個 Mock object 的方法調用是否符合預期.

實際上, Mock object 是 Test stub 或 Fake object 一種, 可是 Mock object 有 Test stub/Fake object 沒有的特性, Mock object 能夠很靈活地配置所調用的方法所產生的行爲, 而且它能夠追蹤方法調用, 例如一個 Mock Object 方法調用時傳遞了哪些參數, 方法調用了幾回等.

  • Dummy object: 在測試中並不使用的, 可是爲了測試代碼可以正常編譯/運行而添加的對象. 例如咱們調用一個 Test Double 對象的一個方法, 這個方法須要傳遞幾個參數, 可是其中某個參數不管是什麼值都不會影響測試的結果, 那麼這個參數就是一個 Dummy object.
    Dummy object 能夠是一個空引用, 一個空對象或者是一個常量等.

簡單的說, Dummy object 就是那些沒有使用到的, 僅僅是爲了填充參數列表的對象.

  • Test Spy
    能夠包裝一個真實的 Java 對象, 並返回一個包裝後的新對象. 若沒有特別配置的話, 對這個新對象的全部方法調用, 都會委派給實際的 Java 對象.

mock 和 spy 的區別是: mock 是無中生有地生出一個徹底虛擬的對象, 它的全部方法都是虛擬的; 而 spy 是在現有類的基礎上包裝了一個對象, 即若是咱們沒有重寫 spy 的方法, 那麼這些方法的實現其實都是調用的被包裝的對象的方法.

Test fixture

所謂 test fixture, 就是運行測試程序所須要的先決條件(precondition). 即對被測對象進行測試時鎖須要的一切東西(The test fixture is everything we need to have in place to exercise the SUT). 這個 東西 不僅僅指的是數據, 同時包括對被測對象的配置, 被測對象所須要的依賴對象等.
JUnit4 以前是經過 setUp, TearDown 方法完成, 在 JUnit4這, 咱們可使用@Before 代替 setUp 方法, @After 代替 tearDown 方法.

注意, @Before 在每一個測試方法運行前都會被調用, @After 在每一個測試方法運行後都會被調用.

由於 @Before 和 @After 會在每一個測試方法先後都會被調用, 而有時咱們僅僅須要在測試前進行一次初始化, 這樣的狀況下, 可使用@BeforeClass 和@AfterClass 註解.

測試用例(Test case)

在 JUnit 3中, 測試方法都必須以 test 爲前綴, 且必須是 public void 的, JUnit 4以後, 就沒有這個限制了, 只要在每一個測試方法標註 @Test 註解, 方法簽名能夠是任意的.

測試套件

經過 TestSuit 對象將多個測試用例組裝成一個測試套件, 測試套件批量運行.
經過@RunWith 和@SuteClass 兩個註解, 咱們能夠建立一個測試套件. 經過@RunWith 指定一個特殊的運行器, 幾 Suite.class 套件運行器, 並經過@SuiteClasses 註解, 將須要進行測試的類列表做做爲參數傳入.

JUnit4

HelloWorld 例子

咱們已一個簡單的例子來快速展現 JUnit4 的基本用法.
首先新建一個名爲 JUniTest 的 Maven 工程, 而後添加依賴:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

接着編寫測試套件:

import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class TestJunit {
    @Test
    public void testingCrunchifyAddition() {
        assertEquals("Here is test for Addition Result: ", 30, addition(27, 3));
    }

    @Test
    public void testingHelloWorld() {
        assertEquals("Here is test for Hello World String: ", "Hello + World", helloWorld());
    }

    public int addition(int x, int y) {
        return x + y;
    }

    public String helloWorld() {
        String helloWorld = "Hello +" + " World";
        return helloWorld;
    }
}

隨後使用測試用例:

public class App {
    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(TestJunit.class);
        for (Failure failure : result.getFailures()) {
            System.out.println(failure.toString());
        }
        if (result.wasSuccessful()) {
            System.out.println("Both Tests finished successfully...");
        }
    }
}

這就是一個完整的 JUnit 測試例子了.

定義測試

一個 JUnit 測試是一個在專用於測試的類中的一個方法, 而且這個方法被 @org.junit.Test 註解標註. 例如:

public class TestJunit {
    @Test
    public void testingCrunchifyAddition() {
        assertEquals("Here is test for Addition Result: ", 30, addition(27, 3));
    }
    ...
}

JUnit4 生命週期

JUnit4測試用例的完整的生命週期要經歷以下幾個階段:

  • 類級初始化資源處理

  • 方法級初始化資源處理

  • 執行測試用例中的方法

  • 方法級銷燬資源處理

  • 類級銷燬資源處理

其中, 類級初始化和銷燬資源處理在每個測試用例類這僅僅執行一次, 方法級初始化, 銷燬資源處理方法在執行測試用例這的每一個測試方法中都會被執行一次.

JUnit4 註解

  • @Test (expected = Exception.class) 表示預期會拋出Exception.class 的異常

  • @Ignore 含義是「某些方法還沒有完成,暫不參與這次測試」。這樣的話測試結果就會提示你有幾個測試被忽略,而不是失敗。一旦你完成了相應函數,只須要把@Ignore註解刪去,就能夠進行正常的測試。

  • @Test(timeout=100) 表示預期方法執行不會超過 100 毫秒,控制死循環

  • @Before 表示該方法在每個測試方法以前運行,可使用該方法進行初始化之類的操做

  • @After 表示該方法在每個測試方法以後運行,可使用該方法進行釋放資源,回收內存之類的操

  • @BeforeClass 表示該方法只執行一次,而且在全部方法以前執行。通常可使用該方法進行數據庫鏈接操做,注意該註解運用在靜態方法。

  • @AfterClass 表示該方法只執行一次,而且在全部方法以後執行。通常可使用該方法進行數據庫鏈接關閉操做,注意該註解運用在靜態方法。

@Test 註解

被@Test 標註的方法就是執行測試用例的測試方法, 例如:

public class TestJunit {
    @Test
    public void  myTest() {
        assertEquals("Here is test for Addition Result: ", 30, addition(27, 3));
    }
}

方法myTest 被註解@Test 標註, 表示這個方法是一個測試方法, 當運行測試用例時, 會自動調用這個方法 .

@BeforeClass , @AfterClass, @Before, @After

使用@BeforeClass 和 @AfterClass 兩個註解標註的方法會在全部測試方法執行先後各執行一次
使用@Before 和 @After 兩個註解標註的方法會在每一個測試方法執行先後都執行一次.

TestSuite

若是有多個測試類, 能夠合併成一個測試套件進行測試, 運行一個 Test Suite, 那麼就會運行在這個 Test Suite 中的所用的測試.
例如:

import org.junit.runner.RunWith;  
import org.junit.runners.Suite;  
import org.junit.runners.Suite.SuiteClasses;  
  
@RunWith( Suite.class )  
@SuiteClasses( { JUnitTest1.class, JUnitTest2.class } )  
public class AllTests {

}

在這個例子中, 咱們定義了一個 Test Suite, 這個 Test Suite 包含了兩個測試類: JUnitTest1 和 JUnitTest2, 所以運行 這個 Test Suite 時, 就會自動運行這兩個測試類了.

在 IntelliJ IDEA 中使用 JUnit 4

建立一個名爲 JUnitTest 的 HelloWorld 工程, 添加依賴:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
    </dependency>
</dependencies>

而後建立 src/tests, 在File -> Project Structure -> Modules -> Sources 中, 右鍵選中 tests, 將其設置爲 Test, 此時 tests 目錄就變爲綠色:
圖片描述

而後建立須要進行測試的類:

public class HelloWorld {
    public String sayHello() {
        return "Hello World!";
    }
}

在 HelloWorld 類名上按下 alt + enter 後, 就能夠自動生成測試類了:

clipboard.png
clipboard.png

IntelliJ 在生tests/ 目錄下生成了一個測試類, 咱們能夠添加自動測試內容:

import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class HelloWorldTest {

    @Test
    public void testSayHello() throws Exception {
        HelloWorld helloWorld = new HelloWorld();
        assertEquals(helloWorld.sayHello(), "Hello World!");
    }
}

本文由 yongshun 發表於我的博客, 採用署名-非商業性使用-相同方式共享 3.0 中國大陸許可協議.
非商業轉載請註明做者及出處. 商業轉載請聯繫做者本人
Email: yongshun1228@gmail.com
本文標題爲: Java JUnit 單元測試小結
本文連接爲: https://segmentfault.com/a/11...

相關文章
相關標籤/搜索