為了節(jié)省篇幅,上面的測(cè)試代碼省略了某些內(nèi)容,例如當(dāng)isDivisor方法的第二個(gè)參數(shù)是負(fù)數(shù)時(shí)會(huì)出現(xiàn)什么情況。
用javac *.java命令編譯這個(gè)類(lèi),然后輸入java CalculatorTest運(yùn)行它。
你應(yīng)該得到一個(gè)“不能判斷小于2的數(shù)是否素?cái)?shù)”的運(yùn)行時(shí)異常。大概形式如下,當(dāng)然顯示的行數(shù)36可能因?yàn)槟闾幚鞢alculatorTest.java中空格的方式而有所不同:
Exception in thread "main" java.lang.RuntimeException: isPrime should throw exception for numbers less than 2
at CalculatorTest.main(CalculatorTest.java:36)
換句話說(shuō),F(xiàn)actorCalculator的功能不正確。在isPrime方法前面加一個(gè)判斷可以解決這個(gè)問(wèn)題,判斷參數(shù)小于2時(shí)拋出IllegalArgumentException異常。像下面的一小段代碼:
if (number < 2) {
throw new IllegalArgumentException();
}
把上面的代碼放到FactorCalculator的isPrime方法的前面。為了便于你參考,修改后的isPrime方法列在FactorCalculator.java.v2中,如果你打算直接使用這個(gè)文件,先把它改名為FactorCalculator.java。
增加了檢查后,重新編譯運(yùn)行CalculatorTest,新的CalculatorTest應(yīng)該可以通過(guò)所有測(cè)試。
三、JUnit提供測(cè)試框架的優(yōu)勢(shì)(JUnit Provides Advantages as a Test Framework)
測(cè)試Java類(lèi)的內(nèi)部功能是剛才你做的那些工作了。真正的測(cè)試和剛才的簡(jiǎn)單例子的主要區(qū)別是代碼庫(kù)的大小和復(fù)雜度。在處理一大堆代碼時(shí),你會(huì)需要收集情況報(bào)告。但上面的例子遇到第一個(gè)錯(cuò)誤停止了,它沒(méi)有收集盡可能多的錯(cuò)誤信息,也不能報(bào)告那些測(cè)試可以通過(guò)。如果一個(gè)測(cè)試不通過(guò),把整個(gè)測(cè)試重新編譯、運(yùn)行一遍,那開(kāi)發(fā)過(guò)程肯定會(huì)非常慢。Bug經(jīng)常是相互關(guān)聯(lián)的,而且由各部分代碼交互的地方引起。一次看到多個(gè)錯(cuò)誤可以幫你分析和解決bug,對(duì)有關(guān)聯(lián)的bug的處理也會(huì)加快。
在使用JUnit重寫(xiě)這個(gè)測(cè)試之前,你需要了解下述術(shù)語(yǔ)和測(cè)試概念:
1、單元測(cè)試(Unit test):?jiǎn)卧獪y(cè)試是指一小段代碼——絕大多數(shù)情況下都只有一個(gè)Java類(lèi)——測(cè)試一個(gè)軟件或者庫(kù)非常有限的一個(gè)部分。單元測(cè)試檢驗(yàn)的代碼都很小,例如一個(gè)或幾個(gè)類(lèi)。通常是測(cè)試EJB組件和普通Java類(lèi)庫(kù),不管這些類(lèi)在服務(wù)器端(容器)環(huán)境中還是獨(dú)立運(yùn)行。與列表中提到的另一個(gè)概念功能測(cè)試比起來(lái),單元測(cè)試的主要區(qū)別在于,單元測(cè)試的重點(diǎn)是終用戶一般看不到的內(nèi)部組件,而功能測(cè)試的重點(diǎn)是“點(diǎn)擊按鈕”。在JUnit中,單元測(cè)試可能是TestCase類(lèi)中的一個(gè)方法,也可能是整個(gè)TestCase類(lèi)。從大小上講一兩頁(yè)的代碼對(duì)單元測(cè)試應(yīng)該是合適的。如果單元測(cè)試達(dá)到十頁(yè)太夸張了,分成若干個(gè)粒度更細(xì)的測(cè)試會(huì)比較好。
2、功能測(cè)試(Functional test):功能測(cè)試是站在終用戶的角度驗(yàn)證程序的功能是否正確。功能測(cè)試和黑盒測(cè)試是同一個(gè)意思。
3、黑盒測(cè)試(Black box test):黑盒測(cè)試是只根據(jù)對(duì)外發(fā)布的接口或公共約定,而忽略程序內(nèi)部實(shí)現(xiàn)進(jìn)行的測(cè)試。這通常意味著你只知道應(yīng)該輸入什么,只測(cè)試預(yù)期的輸出,不知道程序如何生成這些輸出,性能、邊界影響等其它外部特征也不在你的考慮范圍內(nèi),除非代碼的這些方面特性是公共約定的一部分。如果你開(kāi)發(fā)的軟件庫(kù)中有提供給其它開(kāi)發(fā)者(你的終用戶)使用的API,黑盒測(cè)試顯得尤為重要。這個(gè)重要性不僅僅是指軟件庫(kù)能按公共接口說(shuō)的做,軟件庫(kù)避免公共接口中禁止或省略的內(nèi)容也很重要。如果你開(kāi)發(fā)一個(gè)程序,遵守公共接口也是很重要的,因?yàn)樗鼓愫屯轮g能更有效的合作。
4、白盒測(cè)試(White box test):白盒測(cè)試是在知道代碼如何實(shí)現(xiàn)的情況下測(cè)試一段代碼的功能。當(dāng)你要測(cè)試公共約定中沒(méi)有指定,但很重要的行為,例如性能問(wèn)題時(shí),白盒測(cè)試派上用場(chǎng)了。在測(cè)試某些特別復(fù)雜的算法或業(yè)務(wù)邏輯時(shí),也需要白盒測(cè)試。這種情況下,通過(guò)白盒測(cè)試你可以把注意力集中在可能出現(xiàn)錯(cuò)誤的地方,這在黑盒測(cè)試中由于缺乏對(duì)內(nèi)部情況的了解很難做到。
5、容器內(nèi)測(cè)試(In-container test):容器內(nèi)測(cè)試在servlet或EJB容器內(nèi)部進(jìn)行,因此能更直接的和要測(cè)試的代碼通信。Jakarta Cactus項(xiàng)目實(shí)現(xiàn)了一個(gè)免費(fèi)的測(cè)試工具Cactus,它讓你和要測(cè)試的代碼在同一個(gè)容器內(nèi)執(zhí)行,不管這個(gè)容器是servlet容器還是EJB容器。容器內(nèi)測(cè)試對(duì)黑盒功能測(cè)試沒(méi)什么用,它的作用體現(xiàn)在單元測(cè)試上。
6、測(cè)試用例(Test case):Test case在JUnit中是一組相關(guān)的測(cè)試。Test case表現(xiàn)為繼承junit.framework.TestCase的類(lèi)。Test case通常有多個(gè)方法,每個(gè)方法測(cè)試程序一方面的行為。Test case中的測(cè)試方法習(xí)慣用test作前綴命名,但并不是必須這樣做,只要不與其它方法產(chǎn)生沖突可以。
7、測(cè)試集(test suite):Test suite是一組test case或test suites。它表現(xiàn)為繼承junit.framework.TestSuite的類(lèi)。沒(méi)有任何限制要求test suite只包括test case或只包括test suite,它可以既有test case,又有test suite。一個(gè)test suite的子test suite也可以包括test case和test suite,因此允許嵌套測(cè)試。只要你愿意,你可以建立幾組test case,每一組測(cè)試程序的一個(gè)明確的小方面。一組可以形成一個(gè)test suite。然后你可以把它們綜合到一個(gè)主test suite中,后用這個(gè)主test suite測(cè)試整個(gè)程序,一個(gè)功能點(diǎn)一個(gè)功能點(diǎn)的測(cè)試。
8、測(cè)試啟動(dòng)器(Test runner):Test runner是啟動(dòng)測(cè)試過(guò)程的JUnit類(lèi)。你調(diào)用test runner,它依次執(zhí)行你預(yù)訂的測(cè)試。有幾種辦法可以定義要test runner執(zhí)行的測(cè)試。這些辦法在下面的第五部分“指定要運(yùn)行的測(cè)試”中介紹。JUnit有三種不同的test runners:text、AWT和Swing,類(lèi)名分別是junit.textui.TestRunner、junit.awtui.TestRunner和junit.swingui.TestRunner。