JUnit是一個開源的java自動化單元測試框架撩扒。由 Erich Gamma 和 Kent Beck 與1997年開發(fā)完成鸦列。 Erich Gamma 是 GOF的作者 之一妥色;Kent Beck 則在 XP 中有重要的貢獻搪花。Junit常常被開發(fā)者用來做單元測試的自動化測試框架撮竿。但是按照測試人員的角度來說,Junit更嚴格來講是一個測試驅(qū)動器笔呀,我們不僅僅可以用其來做單元測試幢踏,也是可以以其為核心來做集成測試、系統(tǒng)測試微渠。很多人分析Junit喜歡從開發(fā)角度從設計模式來分析搭幻,但是對于測試人員來說需要的是從中學習自動化測試框架的基本組成部分與結(jié)構(gòu)模式。學習Junit不僅僅是學習其用法敛助,更要從中學習一個自動化測試框架的有哪些組成部分粗卜?各個組成部分之間是如何協(xié)調(diào)運作來形成一個可擴展穩(wěn)健的自動化測試框架。Junit可謂“麻雀雖小纳击,五臟俱全续扔。”焕数,其包含一個自動化測試框架所有的要素部分纱昧,很多的測試框架,千變?nèi)f化其實都離不開一個測試框架必有的組件與模式堡赔。本系列嘗試從一個手工測用例轉(zhuǎn)化為自動化執(zhí)行以后的模式识脆,來總結(jié)出一個自動化測試所必須的一個組成部分,并借用Junit來說明Junit中是如何實現(xiàn)這幾個部分的善已。
基本概述
讓我們從平時做測試的一個簡單情形開始灼捂。想想平時我們是如何做測試的,或者說是如何執(zhí)行測試過程的换团?是不是有用例?我們編寫了用例悉稠,在用例里面說明了測試預置環(huán)境,測試輸出數(shù)據(jù)艘包,執(zhí)行步驟的猛,和預期的結(jié)果耀盗。例如:
拿到這個用例,是不是要一步步按照用例的說明來操作卦尊。在人工測試的情況下類似如下的情況叛拷,
在這個過程當中,測試人員是執(zhí)行者岂却,讀取測試用例內(nèi)容忿薇,操作被測對象,并判斷結(jié)果是否正確躏哩。
那么煌恢,如果要把上述的過程自動化需要如何做呢?在自動化的過程中震庭,人不在了,那么誰來讀取測試用例呢你雌?準確點說由誰來讀取測試用例的文件器联,并逐條執(zhí)行里面的用例呢?首先想到就是寫一個程序婿崭,此程序能讀取文件拨拓,并識別里面的逐條的測試用例,然后執(zhí)行氓栈。這樣的一個程序叫做測試執(zhí)行器(Test Runner)渣磷。
有了執(zhí)行器以后,又面臨一個問題授瘦,在人工操過程中醋界,人是可以識別文件里的文本用例,讀懂它的步驟然后操作提完,但執(zhí)行器就不行了形纺,它是程序他無法識別文本的意思。程序識別程序徒欣,那么自然而然想到的方法就是把測試用例方法化逐样,即把測試用例用程序的方法來表示,即測試方法(Test Method)打肝,因此執(zhí)行器執(zhí)行用例脂新,就轉(zhuǎn)化為執(zhí)行器調(diào)用方法的過程。
我們進一步觀察粗梭,測試方法争便,實時上,一個方法()Test Method 就代表一個測試用例楼吃,一個測試用例主要包含的重要信息始花,就是用例的執(zhí)行環(huán)境是什么妄讯,執(zhí)行步驟,預期值是什么酷宵,等等亥贸。類似的測試方法也需要包含四個步驟,稱為測試四步驟(Four-Phase Test)
- 環(huán)境設置
- SUT調(diào)用
- 結(jié)果驗證
- 環(huán)境清理
回顧之前所述浇垦,在測當中一個重要的步驟就是驗證炕置,手動測試中,肉眼查看運行結(jié)果并和預期對比來得出測試是否通過的結(jié)果男韧。在自動化過程中自然需要這個過程朴摊,讓測試方法自動驗證測試結(jié)果呢?這部自動化測試中稱為斷言(Assert)此虑。
在整個的測試過程當中還包過如何過濾測試方法甚纲、、測試數(shù)據(jù)管理朦前、最終測試結(jié)果報告等等介杆。
如上述,我們總結(jié)一下韭寸,在自動化測試當中需要涉及的部分包括:
- 測試執(zhí)行器(Runner):讀取測試用例春哨、執(zhí)行測試用例、測試報告輸出
- 測試輸入數(shù)據(jù)管理:如果測試需要外部輸入數(shù)據(jù)恩伺,這些數(shù)據(jù)如何組織管理
- 環(huán)境設置(setUp tearDown):主要設置用例執(zhí)行前的環(huán)境赴背、測試結(jié)束后清理測試環(huán)境;
- 結(jié)果斷言(Assert):如何來判斷測試結(jié)果是否符合預期
- 測試組織與流程控制:如何來組織測試用例晶渠,測試用例的過程如何控制
那么作為一個自動化的測試框架凰荚,Junit如何對上述各個組成部分進行處理呢,我們將從一個Junti入門例子開始乱陡,先對Junit做大致介紹浇揩,然后逐一演示Junit是如何實現(xiàn)上述各個部分的?只要大家理解了Junit對上述幾個部分是怎么做的憨颠,后續(xù)學習其他測試驅(qū)動器就可以快速入手胳徽,比如TestNG-在講完Junit后,我們會針對TestNG如何實現(xiàn)上是問題做簡單說明爽彤,只要大家牢固掌握Junit實現(xiàn)上述問題的本質(zhì)养盗,學習testNG也就是分分鐘的事情。
在Eclipse中使用JUnit4
在開始之前我們先把Junit引入到我們的Eclipse的項目中來适篙,在Eclipse項目右鍵選中往核,如下圖所示
在彈出的屬性窗口中,首先在左邊選擇“Java Build Path”嚷节,然后到右上選擇“Libraries”標簽聂儒,之后在最右邊點擊“Add Library…”按鈕虎锚,如下圖所示:
然后在新彈出的對話框中選擇JUnit4并點擊確定,如上圖所示衩婚,JUnit4軟件包就被包含進我們這個項目了窜护。
Junit 入門例子
假設我們有一個計算器類,包含兩個方法非春,add和multiply柱徙。
public class Calculator {
public int add(int one, int another) {
return one + another;
}
public int multiply(int one, int another) {
return one * another;
}
}
對這兩個方法,我們寫兩條happy path的測試用例:
要把這兩條測試用例自動化執(zhí)行奇昙,要涉及前面所述的幾個步驟护侮,首先是要測試用例腳本化,把測試用例的步驟用代碼方法來表示储耐,我們建一個類叫CalculatorTest,包含這兩條測試用例:
public class CalculatorTest {
public void testAdd(){
Calculator calculator = new Calculator();
int sum = calculator.add(6,7);
if(sum == 13) {
System.out.println("add() SUCCESS!");
} else {
System.out.println("add() FAIL!");
}
}
public void testMultiply(){
Calculator calculator = new Calculator();
int product = calculator.multiply(8,9);
if (product == 56) {
System.out.println("multiply() SUCCESS!");
} else {
System.out.println("multiply() FAILs!");
}
}
}
測試方法是不是要執(zhí)行器來執(zhí)行羊初?如果我沒有Junit這樣的框架,我們怎么執(zhí)行上述的測試方法呢?如下也許是一種方法:
public class Client {
public static void main(String[] args) {
CalculatorTest test = new CalculatorTest();
test.testAdd();
test.testMultiply();
}
}
然后再通過某種方式運行這個main 方法什湘,查看打印的輸出凳忙,來驗證測試是成功還是失敗。想想一下禽炬,如果我們有很多的類,每個類都有很多方法勤家,每個方法都要寫很多的分支來判斷結(jié)果腹尖,而且還要寫個main方法一個個去調(diào)用各個類方法,明顯是不可行呢伐脖?如果有一個程序--執(zhí)行器热幔,能夠自動創(chuàng)建一個測試類,并自動調(diào)用里面的各個測試方法那么就簡化很多了讼庇。Junit框架就提供了這樣的功能绎巨,Junit內(nèi)置的執(zhí)行器,能夠讀取一個類蠕啄,并執(zhí)行里面的測試方法场勤,要讓執(zhí)行者自動找到測試方法并調(diào)用,必須要有一定的約定歼跟,比如告訴執(zhí)行器說用test開頭的方法就是測試方法(Junit 3所采用的方法)或者給測試方法某個標注來表示測試方法和媳,那么執(zhí)行器就會按著這個約定去調(diào)用類里的有特定命名方式或者有特定標志的測試方法。我們來Junit來改寫上述的測試:
public class CalculatorTest {
@Test
public void testAdd(){
Calculator calculator = new Calculator();
int sum = calculator.add(6,7);
if(sum == 13) {
System.out.println("add() SUCCESS!");
} else {
System.out.println("add() FAIL!");
}
}
@Test
public void testMultiply(){
Calculator calculator = new Calculator();
int product = calculator.multiply(8,9);
if (product == 56) {
System.out.println("multiply() SUCCESS!");
} else {
System.out.println("multiply() FAILs!");
}
}
}
大家注意到每個測試方多了一個注解@Test哈街,這個注解的作用的就是告訴Junit的Test Runner 這是一個測試方法留瞳,Test Runner就會調(diào)用這個方法。這時候你就不用自己寫一個main來執(zhí)行這些方法骚秦,在Eclipse下右鍵 Run as Junit Test她倘,Junit的測試執(zhí)行器就會自動運行里面的測試方法璧微。
還有兩個地方:一個是設置部分,每個測試方法都創(chuàng)建了一個Calculator硬梁,這個重復的代碼是否可以抽取出來呢前硫?還有判斷結(jié)果部分分支復雜,能否簡化了?Junit提供fixture和斷言Assert相關方案來解決這兩個問題靶溜,看再一次用Junit簡化后的測試代碼:
public class CalculatorTest {
// fixture部分
public Calculator calculator;
@Before
public void setUp(){
calculator = new Calculator();
}
//測試方法
@Test
public void testAdd(){
calculator = new Calculator();
int sum = calculator.add(6,7);
//斷言部分
Assert.assertEquals(13, sum);
}
@Test
public void testMultiply(){
calculator = new Calculator();
int product = calculator.multiply(8,9);
//斷言部分
Assert.assertEquals(72, product);
}
}
上述@Berfore 表示此方法在每個測試方法運行之前運行一次开瞭。Assert.assertEquals判斷實際結(jié)果與預期是否一致。至此罩息,這就是一個簡單完整的Junit測試代碼嗤详。用Junit執(zhí)行器運行后有如下圖的測試結(jié)果展示:
上述的代碼中有疑問的地方先不管,這只是讓大家體會一些一個Junit測試的整體流程瓷炮,各個注解后面會有詳細介紹葱色。這邊講一些測試的統(tǒng)一結(jié)構(gòu)和命名問題。
統(tǒng)一的測試結(jié)構(gòu)
回顧我們的第一個Junit測試娘香,可以總結(jié)出一個良好的測試方法包含四個步驟苍狰,就是上回我們提到的:
- 設置:稱為Fixture
- 執(zhí)行:掉測試方法
- 斷言:判斷結(jié)果是否預期
- 清理:清理測試,相關例子后續(xù)會看到
極力建議再寫測試過程中中應該在每個階段開始時做好注釋烘绽。
測試方法命名規(guī)則
Junit4之前淋昭,注解,Junit必須采用一種方法來區(qū)分普通的方法和測試方法安接,當時采用的辦法標識 以test為前綴的方法名翔忽、public、非static盏檐、void歇式、無參的方法為測試方法,因此胡野,測試方法名稱都testFoo材失,testBar諸如此類。
但是Junit設計師認為這種的命名方式有時候無法真實揭示測試方法真實的用意硫豆,當一個方法對應的測試方法多了以后龙巨,甚至還會造成一些混亂,因此在Junit采用@Test注解來標識測試方法后熊响,Junit取消測試方法名稱必須test開頭的規(guī)定恭应,而是可以隨意命名,但是大部分人還是以test開頭耘眨。
后面有人提出 行為表達式(Behavior-expressing patterns)的命名方式:
[UnitOfWork_StateUnderTest]
下劃線可以用with昼榛、if 等此替換。這種方式的作用是希望能讓測試用例和代碼文檔一樣易于閱讀,比如上述的代碼寫久了胆屿,回頭去看不知道方法testMultiply在乘積溢出情況下會返回什么奥喻,如果你的測試名稱是這么寫的:
multiplyOverflowWithException()
那么看測試方法的人就知道這測試方法,在溢出時會拋異常
集體怎么命名取決與你們的團隊是如何約定非迹,看個人喜好环鲤。
總結(jié)
我們討論手工測試自動化后,需要處理的幾個步驟憎兽,包括測試方法的讀取與執(zhí)行冷离、測試執(zhí)行環(huán)境的設置、測試結(jié)果的判斷纯命、測試報告西剥、測試數(shù)據(jù)的管理、測試用例的組織等方面亿汞,一般的自動化測試框架要解決也就這幾個方面瞭空,對于Junit testNg無不如此。對于Junit 我們將從測試執(zhí)行疗我、斷言咆畏、執(zhí)行器、異常測試吴裤、測試組織等幾個大方面詳細介紹它的使用