轉載自https://blog.csdn.net/sunliduan/article/details/42026509java
說到測試,你們都不會陌生,從咱們開始學習編程開始,就知道測試。測試和編程就像兩個雙胞胎似的,但是,顯然咱們更鐘情於雙胞胎中的一個--編程。一些人可能對測試瞭然於胸,卻匱乏於行動,一些人也可能對測試只是聞其名不知其意。下面這篇博文就是給你們在零基礎上講解一下Java中單元測試的使用。程序員
首先來講說,究竟什麼是單元測試?單元測試是指對軟件中的最小可測試單元進行檢查和驗證。對於單元測試中單元的含義,通常來講,要根據實際狀況去斷定其具體含義,如C語言中單元指一個函數,Java裏單元指一個類,圖形化的軟件中能夠指一個窗口或一個菜單等。能夠說,單元就是人爲規定的最小的被測功能模塊。單元測試是在軟件開發過程當中要進行的最低級別的測試活動,軟件的獨立單元將在與程序的其餘部分相隔離的狀況下進行測試。總的來講,可總結成如下四點:web
1. 人爲規定編程
2. 最小被測功能模塊數組
3. 最低級別maven
4. 不依賴其餘單元函數
知道什麼是單元測試了,咱們立刻就要想到,單元測試有什麼用呢?我爲何要進行單元測試呢?下面從如下四點來講明單元測試的好處:工具
稍有信息素質的專業程序員老是追求着一件事情---寫出優雅的代碼。這裏的優雅,不只僅是指需求功能的準確實現,更是系統上線後的穩定和高性能。而測試用例的認真思考與書寫,就給了程序員一個「深思熟慮」的機會,讓咱們在「作」以前先「想」好了。固然,這可能須要豐富的編程經驗。不過我也相信,經驗是一點點積累來的,因此從如今開始,爲時不晚。性能
咱們之前的測試,基本上都是從web層開始,一條線的測試。首先這種測試須要咱們打包部署後運行整個程序來執行,耗費時間較多;其次也是最重要的,出現錯誤後咱們不能很快的定位是那一層的問題,只有一步一步的斷點調試,方可定位到錯誤,這樣調試的時間是很長的。單元測試
而在Java中的單元測試,通常是對一個類的測試。而這個偏偏讓coder極爲迅速而且準確的定位錯誤的來源---就是本類!所以,極大的減小了咱們調試的時間。
在一個大項目或者關係比較緊密的項目中,頗有可能出現兩個子系統之間的接口依賴,例如此次高校雲平臺的項目,其餘子系統都須要基礎系統爲其提供接口,所以很可能會形成這種狀況,前期開發中基礎系統一直在開發接口,而本身的功能只能放後!
怎麼才能解決這個問題呢?隔離測試!它使得咱們能夠測試還未寫完的代碼(只要你又接口可以使用),另外,隔離測試能幫助團隊單元測試代碼的一部分,而無需等待所有代碼的完成。
知道什麼是單元測試,而且單元測試有什麼用了,下面咱們就舉幾個很簡單的例子來講明一下單元測試該怎麼用!網上一查,單元測試的工具類有不少,再此我選用最流行的Junit
和Mockito
進行測試演示。、
先來講說準備條件吧:
首先是引入相關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)); } }
經過一步步的講解,不知道你對單元測試瞭解多少了呢?不論是普通測試,仍是參數化測試,仍是隔離測試,咱們新接觸時確定不可能寫的特別完善。寫測試和寫代碼是同樣的,都須要咱們一點一滴經驗的積累。因此不要以爲測試類難或者麻煩,慢慢來,習慣都是培養出來的。一句很老套的話:學習--就是一個過程~~