【高效編碼】超全面的,超正的單元測(cè)試框架Junit的使用姿勢(shì)

文章目錄

    單元測(cè)試有啥用?
        Junit4的簡(jiǎn)介
        Junit的使用
        相關(guān)注解的說(shuō)明
        斷言
    擴(kuò)展延伸
    總結(jié)
    參考

單元測(cè)試有啥用?

學(xué)習(xí)一個(gè)技術(shù)或者一個(gè)框架,我們首先會(huì)考慮的一個(gè)問(wèn)題這個(gè)框架有啥用,能解決什么問(wèn)題?只有能提升生產(chǎn)力的工具才是好工具。針對(duì)這個(gè)問(wèn)題,想想下面這個(gè)場(chǎng)景,在不用單元測(cè)試的情況下,當(dāng)你編寫(xiě)了一個(gè)很復(fù)雜的方法后,該如何測(cè)試該方法的是不是有bug。除了代碼review和編寫(xiě)一個(gè)main方法手動(dòng)調(diào)用該方法,是不是沒(méi)有其他更好的方法,如果有的話,歡迎讀者朋友們積極留言,讓我學(xué)習(xí)一下。。很明顯上面說(shuō)的兩種方法各有各的弊端,首先是代碼review這種方式,光看不調(diào),對(duì)于一些隱藏很深的bug還是比較難發(fā)現(xiàn)。然后就是編寫(xiě)一個(gè)main方法調(diào)用,如果方法很多的話,都用一個(gè)main方法來(lái)調(diào)用的話,main方法會(huì)非常的龐大,還可能會(huì)出現(xiàn)方法之間相互影響的情況。綜上所述,就要請(qǐng)出我們的單元測(cè)試框架了。讓單元測(cè)試來(lái)大顯身手。
Junit4的簡(jiǎn)介

Junit框架是一種應(yīng)用非常廣泛的單元測(cè)試框架。使用也是非常的方便。其在單元測(cè)試領(lǐng)域有著廣泛的應(yīng)用,主要分為Junit4和Junit5兩個(gè)主要的版本。其中Junit 及其之前的版本是將所有內(nèi)容放到一個(gè)jar中,JUnit 5 由三個(gè)不同的模塊組成。包括JUnit 平臺(tái)、 JUnit Jupiter、 JUnit Vintage。Junit有如下幾個(gè)特點(diǎn):

獨(dú)立于業(yè)務(wù)代碼之外對(duì)業(yè)務(wù)代碼進(jìn)行測(cè)試,這樣的好處就是可以不影響業(yè)務(wù)代碼。
在打包的時(shí)候可以將單元測(cè)試的代碼踢出。
修改業(yè)務(wù)代碼之后可以執(zhí)行單元測(cè)試代碼,看看功能有沒(méi)有bug。
下面以Junit 4為例進(jìn)行說(shuō)明。

Junit的使用

說(shuō)了一堆Junit的介紹和好處,那么Junit該如何使用呢?先舉個(gè)簡(jiǎn)單的栗子。下面定義了一個(gè)待測(cè)試的業(yè)務(wù)類(lèi)Calculate類(lèi),它有加減乘除四個(gè)方法。

public class Calculate {

public int add(int a, int b) {
    return a + b;
}

public int substract(int a, int b) {
    return a - b;
}

public int multiply(int a, int b) {
    return a * b;
}

public int division(int a, int b) {
    return a / b;
}

}

現(xiàn)在要對(duì)這個(gè)類(lèi)里的每個(gè)方法進(jìn)行單元測(cè)試,第一步還是要引入junit依賴(在此默認(rèn)你的項(xiàng)目是通過(guò)maven管理的)。


        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>

在Idea中按下Ctrl+Shift+T快捷鍵就可以創(chuàng)建一個(gè)單元測(cè)試類(lèi)。下面就展示這個(gè)測(cè)試類(lèi)。

@RunWith(JUnit4.class)
public class CalculateTest {
private static Calculate calculate;

private static int A = 4;
private static int B = 0;

@BeforeClass
public static void initClass() {
    System.out.println("針對(duì)所有測(cè)試,只運(yùn)行一次");
    calculate = new Calculate();
}

@Before
public void setUp() {
    System.out.println("測(cè)試方法運(yùn)行前執(zhí)行");
}

@Test
public void add() {
    int add = calculate.add(1, 2);
    Assert.assertEquals(3, add);
    System.out.println("對(duì)add單元測(cè)試完成");
}

@Test
public void substract() {
    int substract = calculate.substract(A, B);
    Assert.assertEquals(4, substract);
    System.out.println("對(duì)substract單元測(cè)試完成");
}

@Test
public void multiply() {
    int multiply = calculate.multiply(A, B);
    Assert.assertEquals(0, multiply);
    System.out.println("對(duì)multiply單元測(cè)試完成");
}

@Test
public void division() {
    //正常情況
    int division = calculate.division(A, 1);
    Assert.assertEquals(4, division);
    System.out.println("對(duì)division單元測(cè)試完成");
}

@Test(expected = ArithmeticException.class)
public void division2() {
    int division = calculate.division(A, B);
    System.out.println("對(duì)異常情況單元測(cè)試完成");
}

}

單元測(cè)試運(yùn)行結(jié)果:

相關(guān)注解的說(shuō)明

單元測(cè)試的執(zhí)行主要是通過(guò)相關(guān)注解來(lái)驅(qū)動(dòng)的。所以,了解各個(gè)注解的作用顯的尤為重要。

@RunWith這個(gè)注解修飾的是測(cè)試類(lèi),作用在類(lèi)上表示該測(cè)試類(lèi)所使用的測(cè)試驅(qū)動(dòng)。
@Test :這個(gè)注解表明被修飾的方法是測(cè)試方法,在Junit中將會(huì)被自動(dòng)執(zhí)行,需要注意的是,測(cè)試方法只能是無(wú)返回值無(wú)參數(shù)的公共方法,即public void 。方法名可以任意取(建議是跟被測(cè)試方法同名),方法不能有入?yún)?。?dāng)然@Test 注解內(nèi)還能傳入一些參數(shù),例如:本例中@Test(expected = ArithmeticException.class) 就是預(yù)期被測(cè)試的方法會(huì)拋出ArithmeticException異常,可以理解異常捕獲。還可以傳入超時(shí)時(shí)間,比如 @Test(timeout = 100):我們給測(cè)試函數(shù)設(shè)定一個(gè)執(zhí)行時(shí)間,超過(guò)了這個(gè)時(shí)間(100毫秒),它們就會(huì)被系統(tǒng)強(qiáng)行終止,并且系統(tǒng)還會(huì)向你匯報(bào)該函數(shù)結(jié)束的原因是因?yàn)槌瑫r(shí),這樣你就可以發(fā)現(xiàn)這些Bug了。
@BeforeClass 這個(gè)注解針對(duì)所有測(cè)試,在所有測(cè)試方法執(zhí)行之前執(zhí)行,且只執(zhí)行一次,且必須是作用在public static void,即修飾靜態(tài)方法,知道原因的小伙伴歡迎留言回答。
@AfterClass 這個(gè)注解針對(duì)所有的測(cè)試,將會(huì)在所有測(cè)試方法執(zhí)行結(jié)束后執(zhí)行一次,且必須作用到public static void。
@Before 初始化方法,每個(gè)測(cè)試方法運(yùn)行之前必須執(zhí)行被@Before注解修飾的方法。
@After 釋放資源,在任何測(cè)試方法執(zhí)行之后需要進(jìn)行的收尾工作,在每個(gè)測(cè)試方案執(zhí)行之后執(zhí)行一次,該注解作用到public static void。
@Ignore 忽略的測(cè)試方法,標(biāo)注的含義就是"某些方法尚未完成,暫不參與此次測(cè)試",被@Ignore注解修飾的方法不會(huì)被執(zhí)行。
在這里插入圖片描述

斷言

一個(gè)完整的單元測(cè)試有三個(gè)步驟:

準(zhǔn)備(測(cè)試環(huán)境,準(zhǔn)備測(cè)試數(shù)據(jù))
執(zhí)行(構(gòu)造請(qǐng)求傳入?yún)?shù)執(zhí)行)
斷言(驗(yàn)證結(jié)果)
前面介紹完了準(zhǔn)備和執(zhí)行兩個(gè)步驟,下面就是介紹斷言這個(gè)步驟了,Junit測(cè)試框架中Assert類(lèi)就是實(shí)現(xiàn)斷言的工具,它主要作用如下:1. 單元測(cè)試用于判斷某個(gè)特定條件下某個(gè)方法的行為;執(zhí)行單元測(cè)試為了證明某段代碼的執(zhí)行結(jié)果和期望的一致。總而言之,斷言的作用就白了就是驗(yàn)證被測(cè)試方法的返回結(jié)果。
Assert類(lèi)中用于斷言的方法有很多,下面介紹幾個(gè)常用的斷言方法。
assertEquals(expected, actual):查看兩個(gè)對(duì)象或者兩個(gè)值是否相等,如果是對(duì)象的話就是使用equal()方法來(lái)比較。參數(shù)expected為用戶期望的值,actual為被測(cè)試方法實(shí)際返回的值。如果這兩個(gè)值相等的話, 則說(shuō)明方法運(yùn)行是正確的。
assertNull()查看對(duì)象是否為空。
assertSame(expected,actual):查看兩個(gè)對(duì)象的引用是否相等,直接用==來(lái)進(jìn)行比較的。比較兩個(gè)對(duì)象的值是否相等。
assertTrue(String message, boolean condition) 要求condition==true,查看運(yùn)行的結(jié)果是否為true。

擴(kuò)展延伸

上面介紹的都是一些基本的概念,實(shí)在是不過(guò)癮,只能算一個(gè)demo級(jí)別的知識(shí)點(diǎn)。那我一個(gè)用Spring容器管理的類(lèi)如何測(cè)試測(cè)試呢?我圖片上傳的方法如何測(cè)試呢?這些都沒(méi)講嘛,沒(méi)意思?。。。」?,讀者朋友們,不要著急,項(xiàng)目級(jí)別的實(shí)戰(zhàn)在后面呢!
SpringBoot項(xiàng)目的單元測(cè)試請(qǐng)看這篇文章一分鐘上手SpringBootTest
SpringMVC項(xiàng)目的單元測(cè)試請(qǐng)看這篇文章 SpringMVC框架如何與Junit整合,看這個(gè)就夠了
總結(jié)

本文是一個(gè)Junit4框架使用的基礎(chǔ)篇,詳細(xì)介紹了Junit4框架的使用,以及Junit框架中各種注解的作用。希望對(duì)讀者朋友們有所幫助。





作者:碼農(nóng)飛哥
微信公眾號(hào):碼農(nóng)飛哥