參數化測試與Mock


參數化測試與Mock

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

單元測試概念

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

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

1. 人為規定

2. 最小被測功能模塊

3. 最低級別

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));
	}
}

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM