首頁>技術>

通常我們寫完程式碼之後,為了確保程式碼的正確性,都需要自己測試一遍,看一下程式碼的執行結果和我們期望的結果是不是一樣的,也就是我們常說的單元測試,java中最常用的單元測試框架是junit,本文主要介紹3個內容:

1、玩轉junit

2、spring整合junit

3、開發工具中使用junit

1、背景

我們寫了一個工具類,有2個方法

package com.javacode2018.junit.demo1; public class MathUtils {    /**     * 獲取最大的數字     *     * @param args     * @return     */    public static int max(int... args) {        int result = Integer.MIN_VALUE;        for (int arg : args) {            result = result > arg ? result : arg;        }        return result;    }     /**     * 獲取最小的數字     *     * @param args     * @return     */    public static int min(int... args) {        int result = Integer.MAX_VALUE;        for (int arg : args) {            result = result < arg ? result : arg;        }        return result;    } }

然後我們想測試這兩個方法,下面我們來寫測試程式碼,如下,測試一下max方法和min方法的結果和我們期望的結果是否一致,不一致的時候,輸出一段文字

package com.javacode2018.junit.demo1; public class MathUtilsTest1 {    public static void main(String[] args) {        testMax();        testMin();    }     public static void testMax() {        int result = MathUtils.max(1, 2, 3);        if (result != 3) {            System.out.println(String.format("max 方法有問題,期望結果是3,實際結果是%d", result));        }    }     public static void testMin() {        int result = MathUtils.min(1, 2, 3);        if (result != 1) {            System.out.println(String.format("min 方法有問題,期望結果是3,實際結果是%d", result));        }    }}

上面我們要測試的方法就2個,若需測試的方法很多的時候,咱們需要寫大量的這種測試程式碼,工作量還是蠻大的,而junit做的事情和上面差不多,都是用來判斷被測試的方法和期望的結果是否一致,不一致的時候給出提示,不過junit用起來更容易一些,還有各種開發用到的ide(eclipse、idea)結合的更好一些,用起來特別的順手。

2、junit用法詳解2.1、使用步驟

1)、新增junit maven配置,這裡我們就用4.13,你們也可以用最新的

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

2)、寫測試用例,在寫好的測試方法上面新增@Test註解,比如我們需要對上面案例中的max方法進行測試,通常我們會新建一個測試類,類名為被測試的類加上Test字尾,即:MathUtilsTest,然後在這個類我們需要寫max方法的測試方法,如下,需要我們在max方法上面加上@Test註解

package com.javacode2018.junit.demo1; import org.junit.Assert;import org.junit.Test; public class MathUtilsTest {    @Test    public void max() throws Exception {        int result = MathUtils.max(1, 2, 3);        //判斷測試結果和我們期望的結果是否一致        Assert.assertEquals(result, 3);    }}

3、執行測試用例,現在測試程式碼都寫好了,下面我們寫個類來啟動測試用例,這裡需要使用JUnitCore.runClasses方法來執行測試用例,如下:

package com.javacode2018.junit.demo1; import org.junit.runner.JUnitCore;import org.junit.runner.Result;import org.junit.runner.notification.Failure; public class Demo1TestRunner {    public static void main(String[] args) {        //使用JUnitCore.runClasses方法傳入測試用例的類,然後獲取測試用例的執行結果        Result result = JUnitCore.runClasses(MathUtilsTest.class);        //獲取失敗的用例        for (Failure failure : result.getFailures()) {            System.out.println(failure);        }        //獲取所有測試用例是否執行成功        System.out.println(result.wasSuccessful());    }}
2.2、同時執行多個測試用例

可以一個測試類中寫多個測試方法,每個方法上加上@Test註解就可以了,然後透過JUnitCore來執行就可以,下面程式碼中我們寫2個方法對MathUtils中的max和min方法都進行測試,我們故意將執行結果和期望結果搞成不一致的,執行下面程式碼,然後看看執行結果。

package com.javacode2018.junit.demo2; import com.javacode2018.junit.demo1.MathUtils;import org.junit.Assert;import org.junit.Test;import org.junit.runner.JUnitCore;import org.junit.runner.Result;import org.junit.runner.manipulation.Ordering;import org.junit.runner.notification.Failure; public class MathUtilsTest2 {    @Test    public void max() throws Exception {        int result = MathUtils.max(1, 2, 3);        //判斷測試結果和我們期望的結果是否一致        Assert.assertEquals(result, 1);    }     @Test    public void min() throws Exception {        int result = MathUtils.min(1, 2, 3);        //判斷測試結果和我們期望的結果是否一致        Assert.assertEquals(result, 3);    }     public static void main(String[] args) {        Result result = JUnitCore.runClasses(MathUtilsTest2.class);         System.out.println("失敗用例個數:" + result.getFailures().size());        for (Failure failure : result.getFailures()) {            System.out.println(failure);        }        System.out.println("執行測試用例個數:" + result.getRunCount());        System.out.println("執行測試用例總耗時(ms):" + result.getRunTime());        System.out.println("測試用例是否都成功了:" + result.wasSuccessful());    }}

執行輸出如下,運行了2個用例,失敗了2個,測試的詳細資訊都被輸出了

失敗用例個數:2max(com.javacode2018.junit.demo2.MathUtilsTest2): expected:<3> but was:<1>min(com.javacode2018.junit.demo2.MathUtilsTest2): expected:<1> but was:<3>執行測試用例個數:2執行測試用例總耗時(ms):11測試用例是否都成功了:false
2.3、使用斷言

什麼是斷言?

斷言是用來判斷程式的執行結果和我們期望的結果是不是一致的,如果不一致,會丟擲異常,斷言中有3個資訊比較關鍵

1、被測試的資料

2、期望的資料

3、丟擲異常

斷言提供的方法將被測試的資料和期望的資料進行對比,如果不一樣的時候,將丟擲異常,程式可以捕獲這個異常,這樣就可以知道測試失敗了。

junit中的org.junit.Assert類中提供了大量靜態方法,用來判斷被測試的資料和期望的資料是否一致,不一致,將丟擲異常,這裡隨便列幾個大家看一下吧

//判斷condition如果不是true,將丟擲異常,異常的提示資訊是messagepublic static void assertTrue(String message, boolean condition)  //判斷expected和actual是否相等,如果不相等,將丟擲異常public static void assertEquals(Object expected, Object actual)

用法,如:

int result = MathUtils.max(1, 2, 3);//判斷測試結果和我們期望的結果是否一致Assert.assertEquals(result, 1);
2.4、測試套件:批次執行測試用例

到目前為止,我們還只能一次執行一個測試類,如下

JUnitCore.runClasses(MathUtilsTest2.class)

但是在實際專案中,我們可能會有很多測試類,需要批次執行。

比如我們有下面2個測試類

MathUtilsTest3001.java

package com.javacode2018.junit.demo3; import com.javacode2018.junit.demo1.MathUtils;import org.junit.Assert;import org.junit.Test; public class MathUtilsTest3001 {    @Test    public void max() throws Exception {        int result = MathUtils.max(1, 2, 3);        //判斷測試結果和我們期望的結果是否一致        Assert.assertEquals(result, 3);    }     @Test    public void min() throws Exception {        int result = MathUtils.min(1, 2, 3);        //判斷測試結果和我們期望的結果是否一致        Assert.assertEquals(result, 1);    }}

MathUtilsTest3002.java

package com.javacode2018.junit.demo3; import com.javacode2018.junit.demo1.MathUtils;import org.junit.Assert;import org.junit.Test;import org.junit.runner.JUnitCore;import org.junit.runner.Result;import org.junit.runner.notification.Failure; public class MathUtilsTest3002 {    @Test    public void max() throws Exception {        int result = MathUtils.max(100, 99, 200);        //判斷測試結果和我們期望的結果是否一致        Assert.assertEquals(result, 200);    }     @Test    public void min() throws Exception {        int result = MathUtils.min(1, -1, 10);        //判斷測試結果和我們期望的結果是否一致        Assert.assertEquals(result, -1);    }}

現在我們希望同時執行上面2個測試類,我們可以這麼做,建立一個AllTest.java類,注意這個類上有2個註解比較特殊,都是junit提供的,@RunWith表示這是一個測試套件類,需要批次執行測試類,具體要執行哪些測試類呢,透過@Suite.SuiteClasses來指定

package com.javacode2018.junit.demo3; import org.junit.runner.RunWith;import org.junit.runners.Suite; @RunWith(Suite.class)@Suite.SuiteClasses({MathUtilsTest3001.class, MathUtilsTest3002.class})public class AllTest {}

下面來個啟動類,將AllTest傳遞給JUnitCore.runClasses

失敗用例個數:0執行測試用例個數:4執行測試用例總耗時(ms):12測試用例是否都成功了:true

測試套件中不僅可以包含基本的測試類,而且可以包含其它的測試套件,這樣可以很方便的分層管理不同模組的單元測試程式碼,比如下面程式碼,Module2Test和Module2Test都是測試套件

@RunWith(Suite.class)@Suite.SuiteClasses({Test1.class, Test2.class})public class Module2Test {} @RunWith(Suite.class)@Suite.SuiteClasses({Test1.class, Test2.class})public class Module2Test {} @RunWith(Suite.class)@Suite.SuiteClasses({Module2Test.class, Module2Test.class, Test3.java})public class AllTest {} //執行AllTestJUnitCore.runClasses(AllTest.class);
2.5、Junit常用註解1)@Test註解

@Test:將一個普通方法修飾成一個測試方法

@Test(excepted=xx.class):xx.class 表示異常類,表示測試的方法丟擲此異常時,認為是正常的測試透過的

@Test(timeout = 毫秒數):測試方法執行時間是否符合預期

2)@BeforeClass

會在所有的方法執行前被執行,static 方法 (全域性只會執行一次,而且是第一個執行)

3)@AfterClass

會在所有的方法執行之後進行執行,static 方法 (全域性只會執行一次,而且是最後一個執行)

4)@Before

會在每一個測試方法被執行前執行一次

5)@After

會在每一個測試方法執行後被執行一次

6)@Ignore

所修飾的測試方法會被測試執行器忽略

7)@RunWith

可以更改測試執行器 org.junit.runner.Runner

下面的案例,基本上用到了上面所有的註解,大家結合輸出理解一下。

package com.javacode2018.junit.demo4; import com.javacode2018.junit.demo1.MathUtils;import org.junit.*;import org.junit.runner.JUnitCore;import org.junit.runner.Result;import org.junit.runner.notification.Failure; import java.util.concurrent.TimeUnit; public class MathUtilsTest4 {     @BeforeClass    public static void bc() {        System.out.println("@BeforeClass");        System.out.println("-----------------");    }     @AfterClass    public static void ac() {        System.out.println("@AfterClass");    }     @Before    public void bf() {        System.out.println("@Before:" + this);    }     @After    public void af() {        System.out.println("@After:" + this);        System.out.println("##################");    }     @Test    public void max() throws Throwable {        System.out.println("max():" + this);        int result = MathUtils.max(1, 2, 3);        //判斷測試結果和我們期望的結果是否一致        Assert.assertEquals(result, 3);    }     @Test    public void min() throws Exception {        System.out.println("min():" + this);        int result = MathUtils.min(1, 2, 3);        //判斷測試結果和我們期望的結果是否一致        Assert.assertEquals(result, 1);    }     //方法執行時間超過了timeout,表示測試用例執行失敗    @Test(timeout = 1000)    public void timeOutTest() throws InterruptedException {        System.out.println("timeOutTest():" + this);        TimeUnit.SECONDS.sleep(2000);    }     //方法若未丟擲expected指定的異常,表示測試用例執行失敗    @Test(expected = NullPointerException.class)    public void expectedTest() {        System.out.println("expectedTest():" + this);        new RuntimeException("異常不匹配");    }     @Test    @Ignore    public void ignoredMethod() {        System.out.println("我是被忽略的方法");    }     public static void main(String[] args) {        Result result = JUnitCore.runClasses(MathUtilsTest4.class);        System.out.println("-----------------");        System.out.println("執行測試用例個數:" + result.getRunCount());        System.out.println("失敗用例個數:" + result.getFailures().size());        for (Failure failure : result.getFailures()) {            System.out.println(failure);        }        System.out.println("執行測試用例總耗時(ms):" + result.getRunTime());        System.out.println("測試用例是否都成功了:" + result.wasSuccessful());     }}

執行結果如下

@BeforeClass-----------------@Before:com.javacode2018.junit.demo4.MathUtilsTest4@78e03bb5timeOutTest():com.javacode2018.junit.demo4.MathUtilsTest4@78e03bb5@After:com.javacode2018.junit.demo4.MathUtilsTest4@78e03bb5##################@Before:com.javacode2018.junit.demo4.MathUtilsTest4@48533e64max():com.javacode2018.junit.demo4.MathUtilsTest4@48533e64@After:com.javacode2018.junit.demo4.MathUtilsTest4@48533e64##################@Before:com.javacode2018.junit.demo4.MathUtilsTest4@7e0b37bcmin():com.javacode2018.junit.demo4.MathUtilsTest4@7e0b37bc@After:com.javacode2018.junit.demo4.MathUtilsTest4@7e0b37bc##################@Before:com.javacode2018.junit.demo4.MathUtilsTest4@1a93a7caexpectedTest():com.javacode2018.junit.demo4.MathUtilsTest4@1a93a7ca@After:com.javacode2018.junit.demo4.MathUtilsTest4@1a93a7ca##################@AfterClass-----------------執行測試用例個數:4失敗用例個數:3timeOutTest(com.javacode2018.junit.demo4.MathUtilsTest4): test timed out after 1000 millisecondsmax(com.javacode2018.junit.demo4.MathUtilsTest4): hah expectedTest(com.javacode2018.junit.demo4.MathUtilsTest4): Expected exception: java.lang.NullPointerException執行測試用例總耗時(ms):1018測試用例是否都成功了:false

從輸出中可以看出

@BeforeClass和@AfterClass標註的方法只會執行一次每個@Test標註的方法執行之前會先執行@Before標註的方法,然後執行@Test標註的這個方法,之後再執行@After從this的輸出看出,每個@Test執行的時候,當前類的例項都會重新建立一個新的不論@Test標註的方法是否異常,@AfterClass、@After標註的方法都會執行,且異常會被淹沒,輸出中看不到異常資訊2.6、引數化測試

Junit 4 引入了一個新的功能引數化測試

引數化測試允許開發人員使用不同的值反覆運行同一個測試,你將遵循 5 個步驟來建立引數化測試

用 @RunWith(Parameterized.class) 來註釋 test 類。建立一個由 @Parameters 註釋的公共的靜態方法,它返回一個物件的集合(陣列)來作為測試資料集合。建立一個公共的建構函式,它接受和一行測試資料相等同的東西。為每一列測試資料建立一個例項變數。用例項變數作為測試資料的來源來建立你的測試用例。

可能大家看了上面的理解,還是比較迷糊。

比如我們com.javacode2018.junit.demo1.MathUtils#max測試下面幾組陣列

1,2,3100,99,8030,-1,100

我們可以這麼寫

package com.javacode2018.junit.demo5; import com.javacode2018.junit.demo1.MathUtils;import org.junit.Assert;import org.junit.Test;import org.junit.runner.JUnitCore;import org.junit.runner.Result;import org.junit.runner.RunWith;import org.junit.runner.notification.Failure;import org.junit.runners.Parameterized; import java.util.ArrayList;import java.util.Arrays;import java.util.List; @RunWith(Parameterized.class)public class MathUtilsTest5 {     public static class TestData {        int[] testData;//測試資料        int expectedValue;//預期的結果         public TestData(int[] testData, int expectedValue) {            this.testData = testData;            this.expectedValue = expectedValue;        }         @Override        public String toString() {            return "TestData{" +                    "testData=" + Arrays.toString(testData) +                    ", expectedValue=" + expectedValue +                    '}';        }    }     private TestData testData;     @Parameterized.Parameters    public static List<TestData> initTestData() {        System.out.println("initTestData()");        //key:期望的結果,value:max方法需要測試的資料        List<TestData> result = new ArrayList<>();        result.add(new TestData(new int[]{1, 2, 3}, 3));        result.add(new TestData(new int[]{100, 99, 80}, 100));        result.add(new TestData(new int[]{30, -1, 100}, 100));        return result;    }     public MathUtilsTest5(TestData testData) {        System.out.println("MathUtilsTest5構造器:" + testData);        this.testData = testData;    }     @Test    public void maxTest() throws Throwable {        System.out.println(this.hashCode() + ",maxTest():" + this.testData);        int result = MathUtils.max(this.testData.testData);        //判斷測試結果和我們期望的結果是否一致        Assert.assertEquals(this.testData.expectedValue, result);        System.out.println("###################");    }     public static void main(String[] args) {        Result result = JUnitCore.runClasses(MathUtilsTest5.class);        System.out.println("-----------------");        System.out.println("執行測試用例個數:" + result.getRunCount());        System.out.println("失敗用例個數:" + result.getFailures().size());        for (Failure failure : result.getFailures()) {            System.out.println(failure);        }        System.out.println("執行測試用例總耗時(ms):" + result.getRunTime());        System.out.println("測試用例是否都成功了:" + result.wasSuccessful());     }}

為了方便大家理解程式碼的執行過程,程式碼中添加了很多日誌輸出,執行結果如下,結合程式碼和輸出,理解很容易

initTestData()MathUtilsTest5構造器:TestData{testData=[1, 2, 3], expectedValue=3}721748895,maxTest():TestData{testData=[1, 2, 3], expectedValue=3}###################MathUtilsTest5構造器:TestData{testData=[100, 99, 80], expectedValue=100}463345942,maxTest():TestData{testData=[100, 99, 80], expectedValue=100}###################MathUtilsTest5構造器:TestData{testData=[30, -1, 100], expectedValue=100}195600860,maxTest():TestData{testData=[30, -1, 100], expectedValue=100}###################-----------------執行測試用例個數:3失敗用例個數:0執行測試用例總耗時(ms):12測試用例是否都成功了:true
3、Spring整合junit

spring整合junit比較簡單,下面我們來個案例感受一下。

3.1、加入maven配置
<dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-test</artifactId>    <version>5.2.6.RELEASE</version></dependency><dependency>    <groupId>junit</groupId>    <artifactId>junit</artifactId>    <version>4.13</version></dependency>
3.2、來個spring的入口配置類
package com.javacode2018.springjunit; import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration; @Configurationpublic class MainConfig {    @Bean    public String name() {        return "路人甲java";    }     @Bean    public int age() {        return 30;    }}
3.3、來個junit測試類整合spring

下面我們來個測試類,對上面的MainConfig中註冊的2個bean進行測試

上面程式碼中name和age屬性上面都有@Autowired註解,這2個屬性會被自動從spring容器中注入進來。

3.4、來個測試啟動類
package com.javacode2018.springjunit; import org.junit.runner.JUnitCore;import org.junit.runner.Result;import org.junit.runner.notification.Failure; public class TestRunner {     public static void main(String[] args) {        Result result = JUnitCore.runClasses(MainConfigTest.class);        System.out.println("-----------------");        System.out.println("執行測試用例個數:" + result.getRunCount());        System.out.println("失敗用例個數:" + result.getFailures().size());        for (Failure failure : result.getFailures()) {            System.out.println(failure);        }        System.out.println("執行測試用例總耗時(ms):" + result.getRunTime());        System.out.println("測試用例是否都成功了:" + result.wasSuccessful());     } }

執行輸出

30路人甲java-----------------執行測試用例個數:2失敗用例個數:0執行測試用例總耗時(ms):422測試用例是否都成功了:true
4、開發工具中使用junit

上面介紹的所有案例,都是透過main方法中用JUnitCore.runClasses來執行測試用例的,實際上有更簡單的方式。

java的常用開發工具有eclipse和idea,這兩個工具都將junit整合好了,透過開發工具整合的功能,執行測試用例更方便,不需要我們寫JUnitCore.runClasses程式碼了。

我們來演示一下,如下圖

5、總結

1、本文詳細介紹了junit的用法,常用的註解有@Test、@BeforeClass、@AfterClass、@Before、@After、@Ignore、@RunWith,這些都要掌握

2、spring中整合junit,主要的配置就是在測試類上面需要加上下面程式碼

6、案例原始碼
git地址:https://gitee.com/javacode2018/spring-series 本文案例對應原始碼:    spring-series\lesson-008-junit    spring-series\lesson-008-springjunit

大家star一下,所有系列程式碼都會在這個裡面,還有所有原創文章的連結也在裡面,方便查閱!!!

原文連結:https://itsoku.blog.csdn.net/article/details/110675698

16
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 基於springboot+layui旅遊系統的設計與實現