測試的概念
回歸測試框架
JUnit
Design by Contract
Refactoring
IDE對JUnit的支持
JUnit簡介
安裝
Fixture
TestCase
TestSuite
TestRunner
JUnit最佳實踐
JUnit與J
EE
測試的概念
長期以來
我所接觸的軟件開發人員很少有人能在開發的過程中進行測試工作
大部分的項目都是在最終驗收的時候編寫測試文檔
有些項目甚至沒有測試文檔
現在情況有了改變
我們一直提倡UML
RUP
軟件工程
CMM
目的只有一個
提高軟件編寫的質量
舉一個極端的例子
如果你是一個超級程序設計師
一個傳奇般的人物
(你可以一邊喝咖啡
一邊聽著音樂
同時編寫這操作系統中關於進程調度的模塊
而且兩天時間內就完成了!)我真得承認
有這樣的人
(那個編寫UNIX中的vi編輯器的家伙就是這種人
)然而非常遺憾的是這些神仙們並沒有留下如何修成正果的README
所以我們這些凡人--在同一時間只能將注意力集中到若干點(據科學統計
我並不太相信
一般的人只能同時考慮最多
個左右的問題
高手可以達到
個左右)
而不能既縱覽全局又了解細節--只能期望於其他的方式來保證我們所編寫的軟件質量
為了說明我們這些凡人是如何的笨
有一個聰明人提出了軟件熵(software entropy)的概念
一個程序從設計很好的狀態開始
隨著新的功能不斷地加入
程序逐漸地失去了原有的結構
最終變成了一團亂麻
你可能會爭辯
在這個例子中
設計很好的狀態實際上並不好
如果好的話
就不會發生你所說的情況
是的
看來你變聰明了
可惜你還應該注意到兩個問題
)我們不能指望在恐龍紀元(大概是十年前)設計的結構到了現在也能適用吧
)擁有簽字權的客戶代表可不理會加入一個新功能是否會對軟件的結構有什麼影響
即便有影響也是程序設計人員需要考慮的問題
如果你拒絕加入這個你認為致命的新功能
那麼你很可能就失去了你的住房貸款和面包(對中國工程師來說也許是米飯或面條
要看你是南方人還是北方人)
另外
需要說明的是我看過的一些講解測試的書都沒有我寫的這麼有人情味(不好意思
)
我希望看到這片文章的兄弟姐妹能很容易地接受測試的概念
並付諸實施
所以有些地方寫的有些誇張
歡迎對測試有深入理解的兄弟姐妹能體察民情
並不吝賜教
好了
我們現在言歸正傳
要測試
就要明白測試的目的
我認為測試的目的很簡單也極具吸引力
寫出高質量的軟件並解決軟件熵這一問題
想象一下
如果你寫的軟件和Richard Stallman(GNU
FSF的頭兒)寫的一樣有水准的話
是不是很有成就感?如果你一致保持這種高水准
我保證你的薪水也會有所變動
測試也分類
白箱測試
黑箱測試
單元測試
集成測試
功能測試
我們先不管有多少分類
如何分類
先看那些對我們有用的分類
關於其他的測試
有興趣的人可參閱其他資料
白箱測試是指在知道被測試的軟件如何(How)完成功能和完成什麼樣(What)的功能的條件下所作的測試
一般是由開發人員完成
因為開發人員最了解自己編寫的軟件
本文也是以白箱測試為主
黑箱測試則是指在知道被測試的軟件完成什麼樣(What)的功能的條件下所作的測試
一般是由測試人員完成
黑箱測試不是我們的重點
本文主要集中在單元測試上
單元測試是一種白箱測試
目的是驗證一個或若干個類是否按所設計的那樣正常工作
集成測試則是驗證所有的類是否能互相配合
協同完成特定的任務
目前我們暫不關心它
下面我所提到的測試
除非特別說明
一般都是指單元測試
需要強調的是
測試是一個持續的過程
也就是說測試貫穿與開發的整個過程中
單元測試尤其適合於迭代增量式(iterative and incremental)的開發過程
Martin Fowler(有點兒像引用孔夫子的話)甚至認為
在你不知道如何測試代碼之前
就不應該編寫程序
而一旦你完成了程序
測試代碼也應該完成
除非測試成功
你不能認為你編寫出了可以工作的程序
我並不指望所有的開發人員都能有如此高的覺悟
這種層次也不是一蹴而就的
但我們一旦了解測試的目的和好處
自然會堅持在開發過程中引入測試
因為我們是測試新手
我們也不理會那些復雜的測試原理
先說一說最簡單的:測試就是比較預期的結果是否與實際執行的結果一致
如果一致則通過
否則失敗
看下面的例子
//將要被測試的類
public class Car{
public int getWheels() {
return
;
}
}
//執行測試的類
public class testCar {
public static void main(String[] args) {
testCar myTest = new testCar();
myTest
testGetWheels();
}
public testGetWheels () {
int expectedWheels =
;
Car myCar = Car();
if (expectedWheels==myCar
getWheels())
System
out
println(
test [Car]: getWheels works perfected!
);
else
System
out
println(
test [Car]: getWheels DOESN
T work!
);
}
}
如果你立即動手寫了上面的代碼
你會發現兩個問題
第一
如果你要執行測試的類testCar
你必須必須手工敲入如下命令
[Windows] d:>java testCar
[Unix] % java testCar
即便測試如例示的那樣簡單
你也有可能不願在每次測試的時候都敲入上面的命令
而希望在某個集成環境中(IDE)點擊一下鼠標就能執行測試
後面的章節會介紹到這些問題
第二
如果沒有一定的規范
測試類的編寫將會成為另一個需要定義的標准
沒有人希望查看別人是如何設計測試類的
如果每個人都有不同的設計測試類的方法
光維護被測試的類就夠煩了
誰還顧得上維護測試類?另外有一點我不想提
但是這個問題太明顯了
測試類的代碼多於被測試的類!這是否意味這雙倍的工作?不!
)不論被測試類-Car
的 getWheels 方法如何復雜
測試類-testCar 的testGetWheels
方法只會保持一樣的代碼量
)提高軟件的質量並解決軟件熵這一問題並不是沒有代價的
testCar就是代價
我們目前所能做的就是盡量降低所付出的代價
我們編寫的測試代碼要能被維護人員容易的讀取
我們編寫測試代碼要有一定的規范
最好IDE工具可以支持這些規范
好了
你所需要的就是JUnit
一個Open Source的項目
用其主頁上的話來說就是
JUnit是由 Erich Gamma 和 Kent Beck 編寫的一個回歸測試框架(regression testing framework)
用於Java開發人員編寫單元測試之用
所謂框架就是Erich Gamma 和 Kent Beck 定下了一些條條框框
你編寫的測試代碼必須遵循這個條條框框
繼承某個類
實現某個接口
其實也就是我們前面所說的規范
好在JUnit目前得到了大多數軟件工程師的認可
遵循JUnit我們會得到很多的支持
回歸測試就是你不斷地對所編寫的代碼進行測試
編寫一些
測試一些
調試一些
然後循環這一過程
你會不斷地重復先前的測試
哪怕你正編寫其他的類
由於軟件熵的存在
你可能在編寫第五個類的時候發現
第五個類的某個操作會導致第二個類的測試失敗
通過回歸測試我們抓住了這條大Bug
回歸測試框架
JUnit
通過前面的介紹
我們對JUnit有了一個大概的輪廓
知道了它是干什麼的
現在讓我們動手改寫上面的測試類testCar使其符合Junit的規范--能在JUnit中運行
//執行測試的類(JUnit版)
import junit
framework
*;
public class testCar extends TestCase {
protected int expectedWheels;
protected Car myCar;
public testCar(String name) {
super(name);
}
protected void setUp() {
expectedWheels =
;
myCar = new Car();
}
public static Test suite() {
/*
* the type safe way
*
TestSuite suite= new TestSuite();
suite
addTest(
new testCar(
Car
getWheels
) {
protected void runTest() { testGetWheels(); }
}
);
return suite;
*/
/*
* the dynamic way
*/
return new TestSuite(testCar
class);
}
public void testGetWheels() {
assertEquals(expectedWheels
myCar
getWheels());
}
}
改版後的testCar已經面目全非
先讓我們了解這些改動都是什麼含義
再看如何執行這個測試
>import語句
引入JUnit的類
(沒問題吧)
>繼承 TestCase
可以暫時將一個TestCase看作是對某個類進行測試的方法的集合
詳細介紹請參看JUnit資料
>setUp()設定了進行初始化的任務
我們以後會看到setUp會有特別的用處
>testGetWheeels()對預期的值和myCar
getWheels()返回的值進行比較
並打印比較的結果
assertEquals是junit
framework
Assert中所定義的方法
junit
framework
TestCase繼承了junit
framework
Assert
>suite()是一個很特殊的靜態方法
JUnit的TestRunner會調用suite方法來確定有多少個測試可以執行
上面的例子顯示了兩種方法
靜態的方法是構造一個內部類
並利用構造函數給該測試命名(test
name
如 Car
getWheels )
其覆蓋的runTest()方法
指明了該測試需要執行那些方法--testGetWheels()
動態的方法是利用內省(reflection
)來實現runTest()
找出需要執行那些測試
此時測試的名字即是測試方法(test method
如testGetWheels)的名字
JU
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28831.html