參數化測試與Mock

參數化測試與Mock

轉載自https://blog.csdn.net/sunliduan/article/details/42026509java

單元測試概念

說到測試,你們都不會陌生,從咱們開始學習編程開始,就知道測試。測試和編程就像兩個雙胞胎似的,但是,顯然咱們更鐘情於雙胞胎中的一個--編程。一些人可能對測試瞭然於胸,卻匱乏於行動,一些人也可能對測試只是聞其名不知其意。下面這篇博文就是給你們在零基礎上講解一下Java中單元測試的使用。程序員

首先來講說,究竟什麼是單元測試?單元測試是指對軟件中的最小可測試單元進行檢查和驗證。對於單元測試中單元的含義,通常來講,要根據實際狀況去斷定其具體含義,如C語言中單元指一個函數,Java裏單元指一個類,圖形化的軟件中能夠指一個窗口或一個菜單等。能夠說,單元就是人爲規定的最小的被測功能模塊。單元測試是在軟件開發過程當中要進行的最低級別的測試活動,軟件的獨立單元將在與程序的其餘部分相隔離的狀況下進行測試。總的來講,可總結成如下四點:web

1. 人爲規定編程

2. 最小被測功能模塊數組

3. 最低級別maven

4. 不依賴其餘單元函數

知道什麼是單元測試了,咱們立刻就要想到,單元測試有什麼用呢?我爲何要進行單元測試呢?下面從如下四點來講明單元測試的好處:工具

  1. 提升代碼質量

稍有信息素質的專業程序員老是追求着一件事情---寫出優雅的代碼。這裏的優雅,不只僅是指需求功能的準確實現,更是系統上線後的穩定和高性能。而測試用例的認真思考與書寫,就給了程序員一個「深思熟慮」的機會,讓咱們在「作」以前先「想」好了。固然,這可能須要豐富的編程經驗。不過我也相信,經驗是一點點積累來的,因此從如今開始,爲時不晚。性能

  1. 減小調試時間

咱們之前的測試,基本上都是從web層開始,一條線的測試。首先這種測試須要咱們打包部署後運行整個程序來執行,耗費時間較多;其次也是最重要的,出現錯誤後咱們不能很快的定位是那一層的問題,只有一步一步的斷點調試,方可定位到錯誤,這樣調試的時間是很長的。單元測試

而在Java中的單元測試,通常是對一個類的測試。而這個偏偏讓coder極爲迅速而且準確的定位錯誤的來源---就是本類!所以,極大的減小了咱們調試的時間。

  1. 隔離測試

在一個大項目或者關係比較緊密的項目中,頗有可能出現兩個子系統之間的接口依賴,例如此次高校雲平臺的項目,其餘子系統都須要基礎系統爲其提供接口,所以很可能會形成這種狀況,前期開發中基礎系統一直在開發接口,而本身的功能只能放後!

怎麼才能解決這個問題呢?隔離測試!它使得咱們能夠測試還未寫完的代碼(只要你又接口可以使用),另外,隔離測試能幫助團隊單元測試代碼的一部分,而無需等待所有代碼的完成

測試方法

知道什麼是單元測試,而且單元測試有什麼用了,下面咱們就舉幾個很簡單的例子來講明一下單元測試該怎麼用!網上一查,單元測試的工具類有不少,再此我選用最流行的JunitMockito進行測試演示。、

先來講說準備條件吧:

首先是引入相關Jar包--Junit4.11和Mockito-groovy-support1.2。本次高校平臺是使用的Maven進行jar包管理,所以我只須要在項目的pom.xml文件下寫入該jar包便可;若是讀者你的項目沒有使用Maven,那麼就須要手動引入相關jar了。

其次就是創建測試類了,直接右擊實際類--new--Junit test case便可。這裏須要使用Maven的朋友注意,測試類要求放在Mavenproject自動生成的src/test/java文件夾下,這樣每次運行maven的時候都會自動運行該文件下的測試類,而且這些類在打包的時候不會打入,僅爲了測試存在。

有了條件了,下面咱們就要開始填充一下新建的測試類了。

(一)普通測試

待測方法:

//加法
publicint add(inta, int b) {
    returna + b;
}

測試方法:

@Test
publicvoidtestAdd(){
    //--------------------第一種寫法----------------------
    //(1)待測方法的「參數賦值」
    int a =1;
    int b=3;

    //(2)賦值後的「指望值」
    intexpectedReturn=6;

    //(3)調用待測方法,獲得「實際值」
    intactualReturn = firstDemo.add(1, 3);

    //(4)經過斷言,判斷「指望值」和「實際值」是否相等
    assertEquals(expectedReturn,actualReturn);

    //---------------------第二種寫法------------------------
    //assertEquals(4,firstDemo.add(1,3));                
}

其實一個測試方法的書寫就是四個步驟:

(1)參數賦值

(2)寫出指望

(3)獲取實際值

(4)斷言--比較指望值和實際值。

因此其實一個測試方法的填充是很簡單的,咱們只須要把這幾個步驟都寫出來就好啦。固然,在實際項目中,我上述方法太簡單,通常狀況下是不用進行測試的。咱們只須要對一些業務邏輯複雜的方法進行測試便可。這裏舉這個例子,只是爲了讓零基礎的讀者可以很快而且容易的理解單元測試怎麼寫。

(二)參數化測試

上面第一個普通測試,是針對一個方法只須要一個測試用例便可完成測試的狀況。在實際項目中,咱們會遇到一些分支語句,這時候一個測試用例已經不能知足咱們覆蓋所有分支語句了。因此咱們就須要寫多個測試用例,但是咱們必須針對這個待測方法,寫多個測試方法嗎?這也太麻煩了吧!沒有什麼解決辦法嗎?若是是Junit3的話,咱們只能這樣作,但是從Junit4開始,加入了一個新的概念--參數化測試。這就爲咱們解決了這個問題。

參數化測試主要包括五個步驟:

(1)爲準備使用參數化測試的測試類指定特殊的運行器org.junit.runners.Parameterized。

(2)爲測試類聲明幾個變量,分別用於存放指望值和測試所用數據。

(3)爲測試類聲明一個帶有參數的公共構造函數,並在其中爲第二個環節中聲明的幾個變量賦值。

(4)爲測試類聲明一個使用註解org.junit.runners.Parameterized.Parameters修飾的,返回值爲 java.util.Collection的公共靜態方法,並在此方法中初始化全部須要測試的參數對。

(5)編寫測試方法,使用定義的變量做爲參數進行測試。

待測方法:

//多分支語句
public  boolean Parameterization(int a){
    if(a>10) {
     returntrue;
    }else{
     returnfalse;
    }
}

測試方法:

@RunWith(Parameterized.class)//第一步:指定特殊的運行器org.junit.runners.Parameterized
publicclassFirstDemoTestParameterization {
    //要測試的類
    private FirstDemo firstDemo;        
    
    //第二步:爲測試類聲明幾個變量,分別用於存放指望值和測試所用數據。
    privateint input1; 
    private boolean expected;

    @Before //執行每一個測試方法以前都執行一次
    publicvoid setUp() throws Exception {
        firstDemo=newFirstDemo();
    }

    //第三步:帶有參數的公共構造函數,並在其中爲聲明的幾個變量賦值。
    public FirstDemoTestParameterization(intinput1,boolean expected) {
        this.input1= input1;  //參數1
        this.expected= expected;  //期待的結果值
    }
    
   //-------------------(1)參數賦值 &&&(2)寫出指望值----------------------------        

   //第四步:爲測試類聲明一個註解@Parameters,返回值爲Collection的公共靜態方法,並初始化全部須要測試的參數對。
   @Parameters
   publicstaticCollection prepareData() {
       Object[][]object = { { -1,true }, { 13, true } };    //測試數據
       returnArrays.asList(object); //將數組轉換成集合返回
    }
    
    @Test
    publicvoidtestParameterization() {
        //-----------(3)獲取實際值&&&(4)斷言--比較指望值和實際值。---------------        
        //第五步:編寫測試方法,使用定義的變量做爲參數進行測試。
        assertEquals(expected,firstDemo.Parameterization(input1));
    }
}

關於參數化測試,咱們須要注意如下幾點:

(1)@RunWith(Parameterized.class):在每一個須要參數化測試的類上面,咱們都須要寫上@RunWith(Parameterized.class)由於這樣JUnit纔會使用Parameterized運行器來運行測試,不然JUnit會選擇默認的運行器,而默認運行器的不支持參數化測試。

(2)@Parameters:在提供數據的方法上加上一個@Parameters註解,這個方法必須是靜態static的,而且返回一個集合Collection。

(三)隔離測試

隔離測試也是咱們經常使用的一個防止多類之間依賴的測試。最基礎的就是B層對D層的依賴。測試B層時,咱們不可能還要跑D層,這樣的話就不是單元測試。那麼咱們怎麼來解決這個問題呢?咱們不須要跑D層,可是又須要D層的返回值。隔離測試就幫助咱們解決了這個問題。在本次項目中,我選用Mockito來進行隔離測試。

其實說白了,隔離測試,就是一個Mock--模擬的功能。當咱們依賴其餘類時,不須要真實調用,只需模擬出該類便可。具體使用以在下面demo中給出了詳細的解釋。

直接上B層的代碼,待測方法:

publicbooleanIsExist(Student student) {

    ListstudentList = null;
    booleanisExist =true;

    //若輸入學生姓名不爲空
    if(student.getName() != null) {
        //調用Dao層的查詢學生方法,並將信息返回List中
        studentList= studentDao.queryStudent(student.getName());
        //判斷Dao層返回List是否爲空,若是爲空及學生不存在,則返回false,不然返回true
        if(studentList.isEmpty()) {
            isExist=false;
        }else{
            isExist=true;
        }
    }
    returnisExist;
}

測試方法:

packagecom.sld.service;

import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Mockito.when;
import com.sld.dao.StudentDao;
import com.sld.entity.Student;

@RunWith(MockitoJUnitRunner.class)
publicclassStudentServiceTest {

    @Mock  //建立Mock對象,模擬studentDao類
    StudentDaostudentDao;

    @InjectMocks //自動注入Mock類(StudentDao)到被測試類(StudentService),做爲一個屬性
    StudentServicestudentService;

    @Test
    public void testIsExist(){        

        //------------------(一)指望值:隔離Dao層後,本身設定的指望Dao層的返回值-----------

        //(1)實例化一個實體類expectStudent,併爲其賦值
        Student expectStudent = new Student();
        expectStudent.setName("001");

        //(2)實例化一個List集合,並將賦值後的expectStudent實體類放入集合中 
        List<Student> mockStudentList =newArrayList<Student>();
        mockStudentList.add(expectStudent);

        //(3)當調用模擬類的方法時,返回List集合
        when(studentDao.queryStudent(expectStudent.getName())).thenReturn(mockStudentList);

        //------------------(二)實際值:測試Service層方法,而且返回實際值-----------
        Student actualStudent1 = new Student();
        actualStudent1.setName("001");

        //學生信息:存在,應該返回true
        booleanres= studentService.IsExist(actualStudent1);
        System.out.print(res);

        //------------------(三)指望值與實際值比較:測試Service層方法,而且返回實際值-----------
        assertTrue(studentService.IsExist(actualStudent1));

        //------------------補充:爲了對比寫的-----------
        Student actualStudent2 = new Student();
        actualStudent2.setName("002");

        //學生信息:不存在,應該返回false
        booleanres2= studentService.IsExist(actualStudent2);
        System.out.print(res2);
        assertTrue(studentService.IsExist(actualStudent2));     
        //assertTrue(studentService.IsExist(actualStudent2));
    }
}

經過一步步的講解,不知道你對單元測試瞭解多少了呢?不論是普通測試,仍是參數化測試,仍是隔離測試,咱們新接觸時確定不可能寫的特別完善。寫測試和寫代碼是同樣的,都須要咱們一點一滴經驗的積累。因此不要以爲測試類難或者麻煩,慢慢來,習慣都是培養出來的。一句很老套的話:學習--就是一個過程~~

相關文章
相關標籤/搜索