程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Junit 的使用經驗總結

Junit 的使用經驗總結

編輯:關於JAVA

經驗一、不要在測試用例的構造函數中做初始化

當我們需要增加一個測試時,我們要書寫一個自己的測試用例,比如sometest。如果你喜歡在sometest的

構造函數中做有關的初始化工作,這可不是個好習慣。如下例:

public class sometest extends testcase{

public sometest(string testname){

super(testname);

//初始化代碼

}

}

一旦初始化代碼產生異常,比如illegalstateexception,junit隨之將產生一個assertionfailederror,

並顯示類似下面的出錯信息:

junit . framework . assertionfailederror : cannotinstantiatetestcase : test1at

junit . framework . assert . fail ( assert . Java : 143 ) at

junit . framework . testsuite$1 . runtest ( testsuite . Java : 178 ) at

junit . framework . testcase . runbare ( testcase . Java : 129 ) at

junit . framework . testresult$1 . protect ( testresult .Java : 100 ) at

junit . framework . testresult . runprotected ( testresult. Java: 117 ) at

junit . framework . testresult . run ( testresult. Java : 103 ) at

junit . framework . testcase . run( testcase . Java: 120 ) at

junit . framework . testsuite . run( testsuite . Java , compiledcode ) at

junit . ui . testrunner$12 . run (testrunner. Java : 429 )

這一大堆出錯信息只會讓人一頭霧水,我們只知道junit無法實例化某個測試用例,到底出了什麼問題,在

哪兒出錯了呢?不知道!

那麼好的做法是怎樣呢?

答案是重載測試用例的setup()方法進行初始化。當setup()中的初始化代碼產生異常時我們得到的

是類似下面的出錯信息:

java . lang . illegalstateexception : oopsatbp . dtc . setup ( dtc .Java: 34 ) at

junit . framework . testcase . runbare ( testcase .Java: 127 ) at

junit . framework . testresult$ 1 . protect(testresult . Java : 100 ) at

junit . framework . testresult . runprotected ( testresult . Java: 117 ) at

junit . framework . testresult . run ( testresult .Java : 103 )

...

顯然這要清楚得多我們一下子就可以知道是在dtc.Java 的第34 行產生了illegalstateexception

經驗二、不要假定測試用例中測試的執行次序

我們知道在一個junit 的測試用例類中可以包含多個測試,每個測試其實就是一個method。在下面的例子

中有兩個不同的測試,盡管testdothisfirst()在位置上先於testdothissecond(),但我們不能就此假定

testdothisfirst()會先執行。

public class sometestcase extends testcase{

public sometestcase(string testname){

super(testname);

}

public void testdothisfirst(){

...

}

public void testdothissecond(){

}

}

由於junit 內部使用一個vector 來存儲所有的test,因此在不同的操作系統和Java 虛擬機上,test 的執行

次序是不可預測的。

好的習慣是保持測試之間的獨立性,使得它們在任何次序下執行的結果都是相同的。如果真得需要某些測試

按照特定的次序執行,我們可以借助addtest 來實現。如下例:

public static testsuite(){

suite.addtest(new sometestcase(“testdothisfirst”;));

suite.addtest(new sometestcase(“testdothissecond”;));

return suite;

}

這樣我們可以確保junit先執行testdothisfirst(),然後執行testdothissecond()。

經驗三、測試要避免人工干預

如果某段測試代碼需要人工干預,那至少有兩個不良後果:一則不能被包括在自動測試中,比如夜間的回

歸測試;二則不能被重復執行,例如數據刪除的測試不能做完刪除就萬事大吉,比較好的做法是自動補上

刪除掉的數據。經驗二講的是不同的測試要避免相關性,而經驗三講的其實就是測試要避免自相關。

經驗四、在子類中調用父類的setup() 和teardown()讓我們看一看下面的代碼

public class sometestcase extends anothertestcase {

// a connection to a database

private database thedatabase;

public sometestcase (string testname) {

super (testname);

}

public void testfeaturex () {

...

}

public void setup () {

// clear out the database

thedatabase.clear ();

}

}

你發現其中的錯誤了嗎?setup()應該調用super.setup() 以確保anothertestcase 中定義的父類的環境被初

始化了。當然這也有例外,就是基類可以處理任意的測試數據。

經驗五、不要硬性規定數據文件的路徑

我們經常需要從文件系統中讀取測試數據,看下面的代碼:

public void setup () {

fileinputstream inp ("c:\\testdata\\dataset1.dat");

...

}

這段代碼需要把測試數據文件dataset1.dat 放在c:\testdata,這是有問題的。

第一,c 盤可能沒有磁盤空間了測試人員不得不把數據文件放到其他路徑;

第二,可能需要在其他操作系統比如Linux 上執行這一測試。

所以,一個較好的替代方案是

public void setup () {

fileinputstream inp ("dataset1.dat");

...

}

但事實上這樣仍不是很好,因為這要求數據文件的路徑和測試執行的路徑必須是同一個,如果幾個不同

的測試都這樣的話,那要把這些測試集合起來執行就有些困難,我們不得不頻繁的改變當前路徑。

為了解決這個問題,我們可以使用class.getresource()或者class.getresourceasstream(),這樣我

們可以把數據文件放在這個class 的某個相對路徑上。

數據文件應該盡可能和源代碼一起都放在配置管理系統上,但這樣一來如果我們采用上面的resource 機

制,我們就需要做一件工作,就是把數據文件從原來的位置-就是源代碼的某個相對路徑,拷貝到編譯後

的位置,也就是class 文件的相應的相對路徑。這其實並不復雜,因為從class 的package 就可以映射到

Java文件的所在路徑對於Linux或者Windows我們所要做的就是把package中的. 用file.separatorchar 替代。

經驗六、把測試的代碼和被測的代碼放在同樣的目錄下

當我們把測試代碼和被測的代碼放在同一目錄下時,我們就可以在編譯被測代碼的同時編譯測試代碼,從

而確保兩者是同步更新的。事實上當前的普遍做法,就是把單元測試視為build 的一個環節。

經驗七、正確命名測試

把測試用例命名為testclassundertest,比如如果被測的class 是messagelog,那麼測試用例就叫

testmessagelog,這樣做使得測試用例和被測的class一一對應,而在測試用例中每個測試的method 就

可以命名為

testloggingemptymessage()

testloggingnullmessage()

testloggingwarningmessage()

testloggingerrormessage()

同樣是為了說清楚測試的是什麼。正確的命名可以幫助測試代碼的閱讀者了解每個測試的目的。

經驗八、書寫測試時要考慮地區和國家設置

比如某個測試要使用日期,下面的代碼就是創建日期對象的一種方法

Date date = DateFormat.getInstance ().parse ("dd/mm/yyyy");

但是如果運行上面測試代碼的機器采用不同的地區國家設置,那麼就會有問題。因此我們最好用下面的另

一種方法:

Calendar cal = Calendar.getInstance ();

Cal.set (yyyy, mm-1, dd);

Date date = Calendar.getTime ();

顯然,第二種方法能夠適應地區國家設置的變化。

經驗九、利用Junit 的自動異常處理書寫簡潔的測試代碼

很多Junit 的初學者經常會寫出類似下面的這些代碼

public void exampleTest () {

try {

// do some test

} catch (SomeApplicationException e) {

fail ("Caught SomeApplicationException exception");

}

}

事實上在Junit 中使用try-catch 來捕獲異常是沒有必要的,Junit 會自動捕獲異常。那些沒有被捕獲的異常就

被當成錯誤處理。所以上面的代碼很冗余,完全可以寫成下面等效卻簡潔得多的代碼:

public void exampleTest () throws SomeApplicationException {

// do some test

}

更少的測試代碼也更容易讀懂更容易維護。如果需要測試異常是否能被拋出則不能簡化。

經驗十、充分利用Junit 的assert/fail 方法

Junit 有豐富而靈活的assert/fail 方法,如何用好這些方法也是大有講究的。比如下面的寫法就不大好

assert (creds == 3);

不如寫成

assertEquals ("The number of credentials should be 3", 3, creds);

第二種寫法不僅易於閱讀,而且在執行時如果fail 也可以向測試人員提供更多的信息。

Junit 也有支持浮點數的assert方法,干淨利索如下例

assertEquals ("some message", result, expected, delta);

另外要一提的是:

assertSame()用來測試兩個引用是否指向同一個對象

assertEquals()用來測試兩個對象是否相等

經驗十一、確保測試代碼與時間無關

盡量避免可能過期的測試數據,這種數據應該可以手工或者自動的刷新。另外還有一個技巧就是在使用這些

數據前更改系統的當前日期,數據操作結束後再恢復日期。當然,使用這一技巧要注意可能的副作用。

經驗十二、使用文檔生成器做測試文檔

我們當然可以使用文本編輯器來書寫單元測試的文檔,但是更好的方法是使用文檔生成器比如JavaDoc自

動生成,這樣我們就不需擔心實現和文檔之間的同步問題。自動生成的文檔格式統一錯誤也少。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved