[JUnit] 基于JUnit從零開始認(rèn)識(shí)單元測(cè)試

@TOC

手機(jī)用戶請(qǐng)橫屏獲取最佳閱讀體驗(yàn),REFERENCES中是本文參考的鏈接,如需要鏈接和更多資源,可以關(guān)注其他博客發(fā)布地址。

平臺(tái) 地址
CSDN https://blog.csdn.net/sinat_28690417
簡(jiǎn)書 http://m.itdecent.cn/u/3032cc862300
個(gè)人博客 https://yiyuery.club

基于JUnit從零開始認(rèn)識(shí)單元測(cè)試

JUnit預(yù)備知識(shí)

什么是軟件測(cè)試?

  • 軟件測(cè)試是檢查實(shí)際結(jié)果與預(yù)期結(jié)果是否匹配并確保軟件系統(tǒng)無(wú)缺陷的活動(dòng)。
  • 軟件測(cè)試還有助于識(shí)別產(chǎn)品與實(shí)際需求不符或是缺失項(xiàng)。
  • 測(cè)試活動(dòng)既可以手動(dòng)完成,也可以使用自動(dòng)化工具完成。
  • 有些人更喜歡將軟件測(cè)試稱為白盒和黑盒測(cè)試

什么是軟件測(cè)試目標(biāo)?

  • 在給定的產(chǎn)品中盡可能多地發(fā)現(xiàn)錯(cuò)誤(或bug)。
  • 演示一個(gè)給定的軟件產(chǎn)品與它的需求規(guī)格匹配。
  • 使用最小的成本和努力來(lái)驗(yàn)證軟件的質(zhì)量。
  • 生成高質(zhì)量的測(cè)試用例,執(zhí)行有效的測(cè)試,并發(fā)布正確和有用的問題報(bào)告。

什么是軟件測(cè)試過(guò)程?

軟件測(cè)試通常分為兩個(gè)主要過(guò)程——驗(yàn)證 & 認(rèn)證。

  • 驗(yàn)證是當(dāng)您的團(tuán)隊(duì)只需要檢查軟件、系統(tǒng)或框架是否符合文檔要求時(shí)的過(guò)程。

  • 認(rèn)證是您的團(tuán)隊(duì)需要驗(yàn)證系統(tǒng)正確性的過(guò)程。在這個(gè)過(guò)程中,您將回顧產(chǎn)品、系統(tǒng),并考慮用戶真正想要什么和已經(jīng)做了什么。

在軟件測(cè)試中,缺陷和錯(cuò)誤之間有區(qū)別,我們應(yīng)該清楚地區(qū)分,以避免誤解問題。

軟件測(cè)試分類

.

單元測(cè)試這是在開發(fā)人員級(jí)別使用的最基本的測(cè)試,測(cè)試人員專注于單元代碼的單個(gè)部分,而它已經(jīng)從任何外部交互或依賴于任何模塊之前被隔離。這個(gè)測(cè)試要求開發(fā)人員檢查他們編寫的最小代碼單元,并證明單元可以獨(dú)立工作。

如果你聽說(shuō)過(guò)測(cè)試驅(qū)動(dòng)開發(fā)(TDD:Test-Driven Development),單元測(cè)試就不陌生。單元測(cè)試是用來(lái)對(duì)一個(gè)模塊、一個(gè)函數(shù)或者一個(gè)類來(lái)進(jìn)行正確性檢驗(yàn)的測(cè)試工作。比如對(duì)函數(shù)abs(),我們可以編寫出以下幾個(gè)測(cè)試用例:

  • 輸入正數(shù),比如1、1.2、0.99,期待返回值與輸入相同;
  • 輸入負(fù)數(shù),比如-1、-1.2、-0.99,期待返回值與輸入相反;
  • 輸入0,期待返回0;
  • 輸入非數(shù)值類型,比如None、[]、{},期待拋出TypeError。

把上面的測(cè)試用例放到一個(gè)測(cè)試模塊里,就是一個(gè)完整的單元測(cè)試。單元測(cè)試通過(guò)后有什么意義呢?

如果單元測(cè)試通過(guò),說(shuō)明我們測(cè)試的這個(gè)函數(shù)能夠正常工作。如果單元測(cè)試不通過(guò),要么函數(shù)有bug,要么測(cè)試條件
輸入不正確,總之,需要修復(fù)使單元測(cè)試能夠通過(guò)。

如果我們對(duì)abs()函數(shù)代碼做了修改,只需要再跑一遍單元測(cè)試,如果通過(guò),說(shuō)明我們的修改不會(huì)對(duì)abs()函數(shù)原有
的行為造成影響,如果測(cè)試不通過(guò),說(shuō)明我們的修改與原有行為不一致,要么修改代碼,要么修改測(cè)試。

這種以測(cè)試為驅(qū)動(dòng)的開發(fā)模式最大的好處就是確保一個(gè)程序模塊的行為符合我們?cè)O(shè)計(jì)的測(cè)試用例。在將來(lái)修改的
時(shí)候,可以極大程度地保證該模塊行為仍然是正確的。

From: 廖雪峰

單元測(cè)試方式

單元測(cè)試可以由兩種方式完成:

  • 人工測(cè)試

    • 手動(dòng)執(zhí)行測(cè)試用例并不借助任何工具的測(cè)試被稱為人工測(cè)試。?消耗時(shí)間并單調(diào):由于測(cè)試用例是由人力資源執(zhí)行,所以非常緩慢并乏味。
    • 人力資源上投資巨大:由于測(cè)試用例需要人工執(zhí)行,所以在人工測(cè)試上需要更多的試驗(yàn)員。
    • 可信度較低:人工測(cè)試可信度較低是可能由于人工錯(cuò)誤導(dǎo)致測(cè)試運(yùn)行時(shí)不夠精確。
    • 非程式化:編寫復(fù)雜并可以獲取隱藏的信息的測(cè)試的話,這樣的程序無(wú)法編寫。
  • 自動(dòng)測(cè)試(借助工具支持并且利用自動(dòng)工具執(zhí)行用例被稱為自動(dòng)測(cè)試。)

    • 快速自動(dòng)化運(yùn)行測(cè)試用例時(shí)明顯比人力資源快。
    • 人力資源投資較少:測(cè)試用例由自動(dòng)工具執(zhí)行,所以在自動(dòng)測(cè)試中需要較少的試驗(yàn)員。
    • 可信度更高:自動(dòng)化測(cè)試每次運(yùn)行時(shí)精確地執(zhí)行相同的操作。
    • 程式化:試驗(yàn)員可以編寫復(fù)雜的測(cè)試來(lái)顯示隱藏信息。

JUnit 簡(jiǎn)介

JUnit 是一個(gè) Java 編程語(yǔ)言的單元測(cè)試框架。JUnit 在測(cè)試驅(qū)動(dòng)的開發(fā)方面有很重要的發(fā)展,是起源于 JUnit 的一個(gè)統(tǒng)稱為 xUnit 的單元測(cè)試框架之一。

JUnit 促進(jìn)了先測(cè)試后編碼的理念,強(qiáng)調(diào)建立測(cè)試數(shù)據(jù)的一段代碼,可以先測(cè)試,然后再應(yīng)用。這個(gè)方法就好比“測(cè)試一點(diǎn),編碼一點(diǎn),測(cè)試一點(diǎn),編碼一點(diǎn)……”,增加了程序員的產(chǎn)量和程序的穩(wěn)定性,可以減少程序員的壓力和花費(fèi)在排錯(cuò)上的時(shí)間。

特點(diǎn)

  • JUnit 是一個(gè)開放的資源框架,用于編寫和運(yùn)行測(cè)試。
  • 提供注釋來(lái)識(shí)別測(cè)試方法。
  • 提供斷言來(lái)測(cè)試預(yù)期結(jié)果。
  • 提供測(cè)試運(yùn)行來(lái)運(yùn)行測(cè)試。
  • JUnit 測(cè)試允許你編寫代碼更快,并能提高質(zhì)量。
  • JUnit 優(yōu)雅簡(jiǎn)潔。沒那么復(fù)雜,花費(fèi)時(shí)間較少。
  • JUnit 測(cè)試可以自動(dòng)運(yùn)行并且檢查自身結(jié)果并提供即時(shí)反饋。所以也沒有必要人工梳理測(cè)試結(jié)果的報(bào)告。
  • JUnit 測(cè)試可以被組織為測(cè)試套件,包含測(cè)試用例,甚至其他的測(cè)試套件。
  • JUnit 在一個(gè)條中顯示進(jìn)度。如果運(yùn)行良好則是綠色;如果運(yùn)行失敗,則變成紅色。

從零開始搭建JUnit測(cè)試環(huán)境

測(cè)試場(chǎng)景

JUnit是一款優(yōu)秀的開源Java單元測(cè)試框架,也是目前使用率最高最流行的測(cè)試框架,開發(fā)工具Eclipse和IDEA對(duì)JUnit都有很好的支持,JUnit主要用于以下測(cè)試場(chǎng)景。

  • 白盒測(cè)試:把測(cè)試對(duì)象看作一個(gè)打開的盒子,程序內(nèi)部的邏輯結(jié)構(gòu)和其他信息對(duì)測(cè)試人?員是公開的;
  • 回歸測(cè)試:軟件或環(huán)境修復(fù)或更正后的再測(cè)試;
  • 單元測(cè)試:最小粒度的測(cè)試,以測(cè)試某個(gè)功能或代碼塊。一般由程序員來(lái)做,因?yàn)樗枰纼?nèi)部程序設(shè)計(jì)和編碼的細(xì)節(jié);

JUnit GitHub地址:https://github.com/junit-team

環(huán)境搭建

  • Spring Boot 2.1.0 RELEASE
  • JUnit 4.12
  • Maven 3.0.0+
  • IDEA 2019.2
testImplementation 'org.springframework.boot:spring-boot-starter-test'
.

Spring 框架

.

Spring Boot 框架

.
.

核心API

JUnit API TestCase

測(cè)試樣例定義了運(yùn)行多重測(cè)試的固定格式

  • int countTestCases() 為被run(TestResult result) 執(zhí)行的測(cè)試案例計(jì)數(shù)
  • TestResult createResult() 創(chuàng)建一個(gè)默認(rèn)的 TestResult 對(duì)象
  • String getName() 獲取 TestCase 的名稱
  • TestResult run() 一個(gè)運(yùn)行這個(gè)測(cè)試的方便的方法,收集由TestResult 對(duì)象產(chǎn)生的結(jié)果
  • void run(TestResult result) 在 TestResult 中運(yùn)行測(cè)試案例并收集結(jié)果
  • void setName(String name) 設(shè)置 TestCase 的名稱
  • void setUp()
    創(chuàng)建固定裝置,例如,打開一個(gè)網(wǎng)絡(luò)連接
  • void tearDown() 拆除固定裝置,例如,關(guān)閉一個(gè)網(wǎng)絡(luò)連接
  • String toString() 返回測(cè)試案例的一個(gè)字符串表示
/*測(cè)試入口*/
public class Ex3Test {

    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(JunitEx3Test.class,JunitEx3_2Test.class);
        for (Failure failure : result.getFailures()) {
            System.out.println(failure.toString());
        }
        System.out.println(result.wasSuccessful());
    }

}
public class JunitEx3_2Test extends TestCase {


    @Test
    public void testXX2() {
        System.out.println("No of Test2 Case:" + this.countTestCases());
        System.out.println("Original Test Name:" + this.getName());
        this.setName("JunitEx3_2Test Name");
        System.out.println("Update Test Name:" + this.getName());
    }
}

public class JunitEx3Test extends TestCase {

    /**
     * 為被run(TestResult result) 執(zhí)行的測(cè)試案例計(jì)數(shù)
     * 注意前綴必須為test***
     */
    @Test
    public void testX1() {
        System.out.println("No of Test Case:" + this.countTestCases());
        System.out.println("Original Test Name:" + this.getName());
        this.setName("testX1 Name");
        System.out.println("Update Test Name:" + this.getName());
    }

    @Test
    public void testX2() {
        System.out.println("No of Test Case:" + this.countTestCases());
        System.out.println("Original Test Name:" + this.getName());
        this.setName("testX2 Name");
        System.out.println("Update Test Name:" + this.getName());
    }
}

JUnit API TestResult

TestResult 類收集所有執(zhí)行測(cè)試案例的結(jié)果。它是收集參數(shù)層面的一個(gè)實(shí)例。這個(gè)實(shí)驗(yàn)框架區(qū)分失敗和錯(cuò)誤。失敗是可以預(yù)料的并且可以通過(guò)假設(shè)來(lái)檢查。錯(cuò)誤是不可預(yù)料的問題就像 ArrayIndexOutOfBoundsException。

TestResult 類的一些重要方法列式如下:

  • void addError(Test test, Throwable t) 在錯(cuò)誤列表中加入一個(gè)錯(cuò)誤
  • void addFailure(Test test, AssertionFailedError t) 在失敗列表中加入一個(gè)失敗
  • void endTest(Test test) 顯示測(cè)試被編譯的這個(gè)結(jié)果
  • int errorCount() 獲取被檢測(cè)出錯(cuò)誤的數(shù)量
  • Enumeration errors() 返回錯(cuò)誤的詳細(xì)信息
  • int failureCount() 獲取被檢測(cè)出的失敗的數(shù)量
  • void run(TestCase test) 運(yùn)行 TestCase
  • int int runCount() 獲得運(yùn)行測(cè)試的數(shù)量
  • void startTest(Test test) 聲明一個(gè)測(cè)試即將開始
  • void stop()標(biāo)明測(cè)試必須停止
/*測(cè)試入口*/
public class Ex4Test {

    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(JEx4_1Test.class);
        for (Failure failure : result.getFailures()) {
            System.out.println(failure.toString());
        }
        System.out.println(result.wasSuccessful());
    }
}

public class JEx4_1Test extends TestResult {

    @Test
    public void testX1() throws IllegalArgumentException {
        throw new IllegalArgumentException("testX1 failed");
    }
    @Test
    public void testX2() throws IllegalArgumentException {
        throw new IllegalArgumentException("testX2 failed");
    }

    @Test
    public void testX3()  {
        System.out.println("testX3 success...");
    }
}

JUnit API TestSuite

TestSuite 類是測(cè)試的組成部分。它運(yùn)行了很多的測(cè)試案例

  • void addTest(Test test) 在套中加入測(cè)試。
  • void addTestSuite(Class<? extends TestCase> testClass) 將已經(jīng)給定的類中的測(cè)試加到套中。
  • int countTestCases() 對(duì)這個(gè)測(cè)試即將運(yùn)行的測(cè)試案例進(jìn)行計(jì)數(shù)。
  • String getName() 返回套的名稱。
  • void run(TestResult result) 在 TestResult 中運(yùn)行測(cè)試并收集結(jié)果。
  • void setName(String name) 設(shè)置套的名稱。
  • Test testAt(int index) 在給定的目錄中返回測(cè)試。
  • int testCount() 返回套中測(cè)試的數(shù)量。
  • static Test warning(String message) 返回會(huì)失敗的測(cè)試并且記錄警告信息。
/*測(cè)試入口*/
public class Ex5Test {

    public static void main(String[] args) {
        TestSuite suite = new TestSuite(TestJunit1.class, TestJunit2.class);
        TestResult result = new TestResult();
        suite.run(result);
        System.out.println("Number of test cases = " + result.runCount());
    }
}

public class TestJunit1 extends TestCase {

    String message = "Robert";
    MessageUtil messageUtil = new MessageUtil(message);

    @Test
    public void testMessage() {
        System.out.println("TestJunit1 testMessage()");
        assertEquals(message, messageUtil.printMessage());
    }
}

public class TestJunit2 extends TestCase {

    String message = "Robert";
    MessageUtil messageUtil = new MessageUtil(message);

    @Test
    public void testMessage() {
        System.out.println("TestJunit2 testMessage()");
        message = "Hi!" + "Robert";
        assertEquals(message,messageUtil.printMessage());
    }
}

JUnit單元測(cè)試如何開展

IDEA支持

IntellJ IDEA 支持快速生成測(cè)試用例

Ctrl + Shift + T

.
.

斷言測(cè)試

測(cè)試結(jié)果的表達(dá)式

斷言測(cè)試也就是期望值測(cè)試,是單元測(cè)試的核心,也就是決定測(cè)試結(jié)果的表達(dá)式

Assert對(duì)象中的斷言方法:
Assert.assertEquals 對(duì)比兩個(gè)值相等
Assert.assertNotEquals 對(duì)比兩個(gè)值不相等
Assert.assertSame 對(duì)比兩個(gè)對(duì)象的引用相等
Assert.assertArrayEquals 對(duì)比兩個(gè)數(shù)組相等
Assert.assertTrue 驗(yàn)證返回是否為真
Assert.assertFlase 驗(yàn)證返回是否為假
Assert.assertNull 驗(yàn)證null
Assert.assertNotNull 驗(yàn)證非null
public class Ex1Test {

    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(Ex1Test.class);
        for (Failure failure : result.getFailures()) {
            System.out.println(failure.toString());
        }
        System.out.println(result.wasSuccessful());
    }

    /**
     * 簡(jiǎn)單輸出測(cè)試 Person(name=xxx)
     */
    @Test
    public void test1() {
        Person person = Person.builder().name("xxx").build();
        System.out.println(person.toString());
    }

    /**
     * 利用 Assert 斷言輸出結(jié)果
     * org.junit.Assert
     */
    @Test
    public void test2() {
        Person person = Person.builder().name("xxx").build();
        System.out.println(person.toString());
        Assert.assertEquals(person.getName(), "xxx");
        Assert.assertSame(person.getName(), "xxx");
        Assert.assertNotSame(person.getName(), "xx2");
        Assert.assertFalse(person.getName().endsWith("xx2"));
        Assert.assertTrue(person.getName().endsWith("xx"));
        Assert.assertNull(null);
        Assert.assertNotNull(person);
    }

    /**
     * 測(cè)試  @Before
     * Person(name=before....1)
     * Person(name=xxx3)
     */
    @Test
    public void test3() {
        Person person = Person.builder().name("xxx3").build();
        System.out.println(person.toString());
    }

    @Before
    public void before1() {
        Person person = Person.builder().name("before....1").build();
        System.out.println(person.toString());
    }

    /**
     * 測(cè)試 @After
     * Person(name=before....1)
     * Person(name=xxx4)
     * Person(name=after...1)
     */
    @Test
    public void test4() {
        Person person = Person.builder().name("xxx4").build();
        System.out.println(person.toString());
    }

    @After
    public void after1() {
        Person person = Person.builder().name("after...1").build();
        System.out.println(person.toString());
    }

    /**
     * 測(cè)試@AfterClass
     * Person(name=before....1)
     * Person(name=xxx5)
     * Person(name=after...1)
     * Person(name=after...2)
     */
    @Test
    public void test5() {
        Person person = Person.builder().name("xxx5").build();
        System.out.println(person.toString());
    }

    /**
     * java.lang.Exception: Method after2() should be static
     * 在所有方法執(zhí)行之后執(zhí)行
     */
    @AfterClass
    public static void after2() {
        Person person = Person.builder().name("after...2").build();
        System.out.println(person.toString());
    }

    /**
     * 測(cè)試 @BeforeClass
     * Person(name=before...2)
     * Person(name=before....1)
     * Person(name=xxx6)
     * Person(name=after...1)
     * Person(name=after...2)
     */
    @Test
    public void test6() {
        Person person = Person.builder().name("xxx6").build();
        System.out.println(person.toString());
    }

    /**
     * java.lang.Exception: Method before2() should be static
     * 在所有方法執(zhí)行之前執(zhí)行
     */
    @BeforeClass
    public static void before2() {
        Person person = Person.builder().name("before...2").build();
        System.out.println(person.toString());
    }

    /**
     * hamcrest-core-1.3.jar
     * Hamcrest是一款用以編寫matcher對(duì)象的框架,以類庫(kù)的形式發(fā)布。一個(gè)matcher對(duì)象就是一個(gè)明確定義的匹配規(guī)則.
     * > 匹配規(guī)則:Matchers
     */
    @Test
    @Ignore
    public void test7() {
        MatcherAssert.assertThat(Long.valueOf(1), instanceOf(Integer.class));
        //字符串匹配符
        String n = "Magci";
        //containsString:字符串變量中包含指定字符串時(shí),測(cè)試通過(guò)
        MatcherAssert.assertThat(n, Matchers.containsString("ci"));
        //startsWith:字符串變量以指定字符串開頭時(shí),測(cè)試通過(guò)
        MatcherAssert.assertThat(n, Matchers.startsWith("Ma"));
        //endsWith:字符串變量以指定字符串結(jié)尾時(shí),測(cè)試通過(guò)
        MatcherAssert.assertThat(n, Matchers.endsWith("i"));
        //euqalTo:字符串變量等于指定字符串時(shí),測(cè)試通過(guò)
        MatcherAssert.assertThat(n, Matchers.equalTo("Magci"));
        //equalToIgnoringCase:字符串變量在忽略大小寫的情況下等于指定字符串時(shí),測(cè)試通過(guò)
        MatcherAssert.assertThat(n, Matchers.equalToIgnoringCase("magci"));
        //equalToIgnoringWhiteSpace:字符串變量在忽略頭尾任意空格的情況下等于指定字符串時(shí),測(cè)試通過(guò)
        MatcherAssert.assertThat(n, Matchers.equalToIgnoringWhiteSpace(" Magci   "));
        //...so on
    }

    @Test
    public void testX() {

    }

    /**
     * fail({{message}}) 直接中止方法運(yùn)行
     */
    @Test
    @Ignore
    public void test8() {
        Assert.fail("directlly stop and output fail log!");
    }
}

套件測(cè)試

測(cè)試套件意味著捆綁幾個(gè)單元測(cè)試用例并且一起執(zhí)行他們。在 JUnit 中,@RunWith 和 @Suite 注釋用來(lái)運(yùn)行套件測(cè)試。這個(gè)教程將向您展示一個(gè)例子,其中含有兩個(gè)測(cè)試樣例 TestJunit1 & TestJunit2 類,我們將使用測(cè)試套件一起運(yùn)行他們。

.
/*測(cè)試入口*/
public class Ex2Test{

    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(TestSuite.class);
        for (Failure failure : result.getFailures()) {
            System.out.println(failure.toString());
        }
        System.out.println(result.wasSuccessful());
    }

}

public class MessageUtil {

    String message;

    public String printMessage() {
        return message;
    }

    public MessageUtil(String message) {
        this.message = message;
    }
}

public class TestJunit1 {

    String message = "Robert";
    MessageUtil messageUtil = new MessageUtil(message);

    @Test
    public void testMessage() {
        System.out.println("TestJunit1 testMessage()");
        assertEquals(message, messageUtil.printMessage());
    }
}

public class TestJunit2 {

    String message = "Robert";
    MessageUtil messageUtil = new MessageUtil(message);

    @Test
    public void testMessage() {
        System.out.println("TestJunit2 testMessage()");
        message = "Hi!" + "Robert";
        assertEquals(message,messageUtil.printMessage());
    }
}

@RunWith(Suite.class)
@Suite.SuiteClasses({
        TestJunit1.class ,TestJunit2.class
})
public class TestSuite {
}

時(shí)間測(cè)試

Junit 提供了一個(gè)暫停的方便選項(xiàng)。如果一個(gè)測(cè)試用例比起指定的毫秒數(shù)花費(fèi)了更多的時(shí)間,那么 Junit 將自動(dòng)將它標(biāo)記為失敗。timeout 參數(shù)和 @Test 注釋一起使用?,F(xiàn)在讓我們看看活動(dòng)中的 @test(timeout)。

.
public class Ex6Test {

    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(JEx6_1Test.class);
        for (Failure failure : result.getFailures()) {
            System.out.println(failure.toString());
        }
        System.out.println(result.wasSuccessful());
    }
}

public class JEx6_1Test {

    @Test
    public void testX1(){
        System.out.println("x1");
    }

    @Test(timeout = 10)
    public void testX2(){
        for(int i =0;i<100000000;i++){
            Math.random();
        }
        System.out.println("x2");
    }

    @Test
    public void testX3(){
        System.out.println("x3");
    }
}

異常測(cè)試

Junit 用代碼處理提供了一個(gè)追蹤異常的選項(xiàng)。你可以測(cè)試代碼是否它拋出了想要得到的異常。expected 參數(shù)和 @Test 注釋一起使用。現(xiàn)在讓我們看看活動(dòng)中的 @Test(expected)。

.
/*測(cè)試入口*/
public class Ex7Test {

    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(JEx7_1Test.class);
        for (Failure failure : result.getFailures()) {
            System.out.println(failure.toString());
        }
        System.out.println(result.wasSuccessful());
    }
}
public class JEx7_1Test {

    String message = "Robert";
    MessageUtil messageUtil = new MessageUtil(message);

    @Test
    public void testX1(){
        System.out.println("x1");
    }

    //@Test(expected = ArithmeticException.class)
    @Test(expected = IllegalArgumentException.class)
    public void testX2(){
        messageUtil.printMessage();
    }

    @Test
    public void testX3(){
        System.out.println("x3");
    }
}

class MessageUtil {

    private String message;

    //Constructor
    //@param message to be printed
    public MessageUtil(String message){
        this.message = message;
    }

    // prints the message
    public void printMessage(){
        System.out.println(message);
        int a =0;
        int b = 1/a;
    }

    // add "Hi!" to the message
    public String salutationMessage(){
        message = "Hi!" + message;
        System.out.println(message);
        return message;
    }
}

參數(shù)化測(cè)試

Junit 4 引入了一個(gè)新的功能參數(shù)化測(cè)試。參數(shù)化測(cè)試允許開發(fā)人員使用不同的值反復(fù)運(yùn)行同一個(gè)測(cè)試。你將遵循 5 個(gè)步驟來(lái)創(chuàng)建參數(shù)化測(cè)試。

  • 用 @RunWith(Parameterized.class) 來(lái)注釋 test 類。
  • 創(chuàng)建一個(gè)由 @Parameters 注釋的公共的靜態(tài)方法,它返回一個(gè)對(duì)象的集合(數(shù)組)來(lái)作為測(cè)試數(shù)據(jù)集合。
  • 創(chuàng)建一個(gè)公共的構(gòu)造函數(shù),它接受和一行測(cè)試數(shù)據(jù)相等同的東西。
  • 為每一列測(cè)試數(shù)據(jù)創(chuàng)建一個(gè)實(shí)例變量。
  • 用實(shí)例變量作為測(cè)試數(shù)據(jù)的來(lái)源來(lái)創(chuàng)建你的測(cè)試用例。

一旦每一行數(shù)據(jù)出現(xiàn)測(cè)試用例將被調(diào)用。

/*測(cè)試入口*/
public class Ex8Test {

    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(JEx8_1Test.class);
        for (Failure failure : result.getFailures()) {
            System.out.println(failure.toString());
        }
        System.out.println(result.wasSuccessful());
    }
}
@RunWith(Parameterized.class)
public class JEx8_1Test {
    private Integer inputNumber;
    private Boolean expectedResult;
    private PrimeNumberChecker primeNumberChecker;

    @Before
    public void initialize() {
        primeNumberChecker = new PrimeNumberChecker();
    }

    public JEx8_1Test(Integer inputNumber,
                                  Boolean expectedResult) {
        this.inputNumber = inputNumber;
        this.expectedResult = expectedResult;
    }

    @Parameterized.Parameters
    public static Collection primeNumbers() {
        return Arrays.asList(new Object[][] {
                { 2, true },
                { 6, false },
                { 19, true },
                { 22, false },
                { 23, true }
        });
    }

    @Test
    public void testPrimeNumberChecker() {
        System.out.println("Parameterized Number is : " + inputNumber);
        assertEquals(expectedResult,
                primeNumberChecker.validate(inputNumber));
    }
}

/**
 * 質(zhì)數(shù)
 */
class PrimeNumberChecker {
    public Boolean validate(final Integer primeNumber) {
        for (int i = 2; i < (primeNumber / 2); i++) {
            if (primeNumber % i == 0) {
                return false;
            }
        }
        return true;
    }
}

事務(wù)控制

.
.
public class Ex1Test extends BaseBootJunitTest {

    @Resource
    private IPersonService personService;

    /**
     * 初始數(shù)據(jù)插入
     */
    @Test
    public void testX1() {
        personService.saveWithName("p_test_1111");
    }


    /**
     * 方法內(nèi)部事務(wù)間隔離
     */
    @Test
    public void testX2() {
        //第一行數(shù)據(jù)插入成功
        personService.saveWithName(CapsuleStringUtil.randomStr("p_test_"));
        //第二行數(shù)據(jù)插入失敗
        personService.saveNoTransactional("p_test_1111");
    }

    /**
     * 針對(duì)Test方法加入事務(wù)控制
     * 避免測(cè)試數(shù)據(jù)污染數(shù)據(jù)庫(kù)
     */
    @Test
    @Transactional(rollbackFor = Exception.class)
    public void testX3() {
        //第一行數(shù)據(jù)插入失敗
        personService.saveWithName(CapsuleStringUtil.randomStr("p_test_"));
        //第二行數(shù)據(jù)插入失敗
        personService.saveNoTransactional("p_test_1111");
    }
}

接口測(cè)試

.
.
public class Ex2Test extends BaseBootJunitTest {

    @Resource
    private TestRestTemplate restTemplate;

    @Test
    public void testHello() {
        String resp = restTemplate.getForObject("/hello", String.class);
        System.out.println(resp);
        Assert.assertEquals("Hello Junit!", resp);
    }
}

忽略測(cè)試

.

.

框架擴(kuò)展

Hamcrest匹配器的用法

.

官網(wǎng)地址 http://hamcrest.org/JavaHamcrest/tutorial

.
.

自動(dòng)化測(cè)試

.

/*測(cè)試入口*/
public class Ex3Test {

    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(JEx3_1Test.class);
        for (Failure failure : result.getFailures()) {
            System.out.println(failure.toString());
        }
        System.out.println(result.wasSuccessful());
    }
}

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class JEx3_1Test extends BaseBootJunitTest {

    @Resource
    private IPersonService personService;

    private String pName;

    /**
     * 每個(gè)測(cè)試方法都是個(gè)測(cè)試用例,獨(dú)立執(zhí)行 @beforeInit 注解標(biāo)注的方法
     */
    @Before
    public void before() {
        System.out.println("[Before]-----------------------------------");
        pName = "p_test_auto_1";
    }

    @After
    public void after() {
        System.out.println("[After]-----------------------------------");
    }

    /**
     * 正常過(guò)程:人員添加
     */
    @Test
    public void testX() {
        Person person = personService.saveWithName(pName);
        log("添加后人員信息返回結(jié)果", person);
        Person dbPerson = personService.findByPersonName(pName,true);
        Assert.assertNotNull(dbPerson);
    }

    /**
     * 正常過(guò)程:人員修改
     */
    @Test
    public void testX2() {
        Person oldPerson = personService.findByPersonName(pName,true);
        oldPerson.setName(pName + "_modify");
        Person newPerson = personService.saveWithResult(oldPerson);
        log("修改后人員信息", newPerson);
        Assert.assertNotNull(newPerson);
        Assert.assertEquals(oldPerson.getName(), newPerson.getName());
    }

    /**
     * 正常過(guò)程:人員刪除
     */
    @Test
    public void testX3() {
        Person dbPerson = personService.findByPersonName(pName+"_modify",true);
        personService.delete(dbPerson.getId());
        log("刪除前人員信息", dbPerson);
        Person personDel = personService.findByPersonName(dbPerson.getName(), false);
        log("刪除后搜索結(jié)果", personDel);
    }
}

總結(jié)

本文從軟件測(cè)試為討論的切入點(diǎn),介紹了單元測(cè)試在軟件測(cè)試中的重要性和對(duì)應(yīng)角色。接下來(lái),以JUnit測(cè)試框架展開,就環(huán)境搭建、測(cè)試類型、JUnit 核心API、JUnit各種測(cè)試方法分別進(jìn)行了介紹,并提供了代碼示例。最后,結(jié)合人員的增刪改操作,編寫了對(duì)應(yīng)的自動(dòng)化測(cè)試用例。

REFRENCES

更多

掃碼關(guān)注“架構(gòu)探險(xiǎn)之道”,回復(fù)本文標(biāo)題或關(guān)鍵詞,獲取本文源碼

.

知識(shí)星球(掃碼加入獲取歷史源碼和文章資源鏈接)

.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容