TDD,耳熟能詳的字眼,在幾年前寫VB Com組件的時候其實就開始重視測試的問題,而在TDD名詞正式冒出之后自己其實也一直宣稱去實踐,但回顧下來,以前做TDD的單元測試一方面原因經常是堅持不下來,另一方面原因是沒擺正思想,不過現在去回看以前寫的單元測試,根本就不能說是TDD,根本就是Class For Test。
TDD,名詞沒什么值得大解釋的,核心思想就是以測試的方式來驅動開發,按照這個思想其實自然就可以引出TDD的實施方法,TDD的目的主要是為了保證類是為了其所賦予的功能、職責而存在,而不是毫無意義的存在,另一方面也是為了保證避免過度設計,還有就是為了將來的重構方便,為了持續集成存在能夠有意義,^_^
TDD的實施方法最重要的就是堅持測試驅動開發的方式,那么具體如何操作呢:
1、編寫一個類的實現前首先編寫測試類。
2、在編寫測試類時需要注意幾點,要想清楚要測的類提供的是什么功能,輸入的參數是什么,輸出的是什么,會有哪些異常情況。
3、在想清楚上面的東西后其實就可以對類寫單元測試了,對于一個類而言通常都是外部輸入的參數決定了其功能的執行以及異常的產生,所以在編寫測試方法的時候應該更多的采用諸如testWhen參數是什么()這樣的命名,測試中的重點就是當參數為某個值時類執行后的輸出是否符合預期,可能這個時候會有疑問產生,就是有些類是沒輸出的,這個我是不認同的,一個類必然都會有輸出,只是有些可能是輸出到數據庫之類的第三方中,但對于編寫測試代碼來說,這并不重要,關鍵是要確定輸出的到底是什么,在測試類中只是要去判斷其輸出是否和預期的一致,在一個測試類中一般都要包含對于類正常情況的測試和否定情況的測試。
4、在編寫完對于類的測試后,這個時候開始運行單元測試,肯定是錯誤的,那么這個時候就開始去編寫實現類,不斷的補充,直到單元測試通過為止,在單元測試通過后也就意味著這個類就寫完了。
寫單元測試的好處:
1、對通過了單元測試的代碼有信心。
2、重構時不怕重構會產生新的問題。
3、對集成測試有保證。
4、保證了寫出來的類是符合需求的。
可能這樣說仍然是很懸乎,在這里我以在Java中TDD方式編寫一個用戶登錄類作為例子來講講:
1、建立用戶登錄類。
2、建立用戶登錄類的單元測試類。
3、思考用戶登錄類的輸入、功能、輸出、各種輸入以及異常情況。用戶登錄類的輸入為用戶名以及用戶密碼,功能為根據用戶名以及用戶密碼校驗用戶是否能夠登錄系統,輸出為登錄成功與否,輸入以及異常情況會有:當用戶名以及用戶密碼都正確的情況、當用戶名、用戶密碼兩個中一個不正確的情況、當用戶名、用戶密碼都為null的情況。
4、根據上面的思考,開始編寫測試類中的方法,需要測試的主要是輸入以及異常情況下類的執行是否符合預期:
4.1 當用戶名以及用戶密碼都正確的情況
在這種情況下,期待登錄類執行的輸出為成功,那么測試類如下:
public void testWhenUsernameAndPasswordAreCorrect(){
用戶登錄類 action=new 用戶登錄類();
action.setUsername("name");
action.setPassword("pass");
assertEquals(true,action.execute());
}
4.2 當用戶名、用戶密碼兩個中一個不正確的情況
在這種情況下,期待登錄類執行的輸出為失敗,那么測試類如下:
public void testWhenUsernameOrPasswordIsError(){
用戶登錄類 action=new 用戶登錄類();
action.setUsername("name");
action.setPassword("jjfwe");
assertEquals(false,action.execute());
}
假設這個時候期待的是登錄類拋出異常,那么測試類只要改成:
try{
action.execute();
}
catch(異常 e){
// 判斷此異常是否為預期的異常
}
4.4 當用戶名、用戶密碼都為null的情況
在這種情況下,假設期待登錄類執行的輸出為失敗,那么測試類如下:
public void testWhenUsernameOrPasswordIsError(){
用戶登錄類 action=new 用戶登錄類();
action.setUsername(null);
action.setPassword(null);
assertEquals(false,action.execute());
}
5、運行測試類。這個時候運行測試類肯定是報錯,因為用戶登錄類還沒有實現,這個時候就開始去用戶登錄類中編寫實現,邊寫邊測試,直到此測試類通過為止,那么用戶登錄類也就寫完了。
上面舉的是一個非常簡單的例子,但已經可以開始看出部分的好處了:
1、保證了類的功能是被實現的,滿足幾種情況下,如將來出現新的情況時再補充。
2、類的實現中不管你是采用數據庫登錄還是LDAP登錄(重構),只要測試是能通過的就OK。
3、強迫coder以用戶的角度去考慮問題。
對于測試類而言,最重要的就是測試在一定的情況下類的執行是否符合預期,這就OK了,所以其實測試類也不會非常難寫,很多人都會覺得寫測試類完全是耽誤時間,這個想法是不太正確的,即使是一個再優秀、再出色的coder寫代碼也是會出錯的,何必讓這種錯誤都要等到系統集成后才冒出來呢,那個時候付出的代價我也想會遠遠超過寫測試類消耗的那點時間,而且通常直接寫實現的話很多時候會出現偏離需求的現象,從大層面去看所有人都明白系統是應該符合需求的,其實對于類同樣也是如此,而且當測試類寫出來了后,寫實現類通常是一件很快的事。
在寫測試類時有幾點是非常值得注意的:
1、測試類中如需要使用來源于數據庫的數據,這時應該保證測試數據和運行數據的分離,不要去依賴系統中真實的運行數據,而是采用測試數據的方法,測試數據要保證在測試類執行完畢后清除,要做好掃尾工作,不要影響到實際的系統。
2、測試代碼和運行代碼的分開,這個在java project通常做法就是建立src/java、src/test的兩種目錄,分開存放。
3、在增加新的功能或原有功能發生變化時,首先應該是去編寫或修改測試類。
.... 寫著寫著忘了其他的幾點,呵呵
^_^,對于TDD有疑問的話,請不要一味的懷疑,何不先試試呢?