在本篇文章中我們將介紹軟件工程師都可以使用的12個單元測試技巧,適用于任何編程語言和編程環(huán)境。
1、使用單元測試降低風(fēng)險
新手可能會問“為什么我應(yīng)該編寫測試代碼?”的確,很多人會這樣想:測試工作不是應(yīng)該由進(jìn)行驗收工作的測試人員來完成嗎?這種想法在現(xiàn)代軟件工程學(xué)中已經(jīng)沒有立足之地。軟件團隊的目標(biāo)是開發(fā)高質(zhì)量的軟件。無論是個人用戶,還是企業(yè)用戶,已經(jīng)無法接受上世紀(jì)80年代和90年代充滿漏洞的軟件。
現(xiàn)在你可以使用豐富的類庫資源、Web服務(wù)和支持重構(gòu)及單元測試的綜合開發(fā)環(huán)境,在軟件中再出現(xiàn)漏洞就沒有任何借口可找了。
單元測試背后的思想是,為每一個軟件單元、模塊和構(gòu)建創(chuàng)建一個測試代碼。單元測試讓軟件持續(xù)測試變得很簡單;與手動測試不同,你可以輕松的重復(fù)執(zhí)行單元測試。
隨著你的軟件規(guī)模變大,單元測試部分也隨之變大。每一個測試都是系統(tǒng)正常運行的保障。代碼中存在漏洞就意味著軟件具有潛在風(fēng)險。通過利用一系列單元測試,開發(fā)者可以大大降低漏洞的數(shù)量,降低未經(jīng)實際運行驗證的程序的風(fēng)險。
2、為每一個主要構(gòu)件編寫一個測試用例
當(dāng)人們開始使用單元測試時,常常會先問“我應(yīng)該編寫什么測試?”
人們最初的想法可能是要編寫大量的功能測試,也就是對系統(tǒng)不同的功能進(jìn)行測試驗證。其實這種想法這是不對的。正確的做法應(yīng)該是為每一個主要構(gòu)件創(chuàng)建一個測試用例。
測試的重點應(yīng)該是一個構(gòu)件。在每一個構(gòu)件內(nèi)找到它的所有接口,也就是這個組件對外公開的方法集。然后你才應(yīng)該為每一個公開的方法編寫一個測試。
3、創(chuàng)建抽象測試用例和添加測試工具
無論任何程序,都有一些共性的東西需要你對其進(jìn)行測試。那么你首先應(yīng)該為你的語言尋找一個單元測試。
舉個例子來說,很多Java開發(fā)者使用Junit來進(jìn)行單元測試,它是一個簡單但強大的測試框架。它具有一個TestCase類,是所有測試的基類。
在你的開發(fā)環(huán)境中增加方便的方法和可用的工具。這樣所有你的測試用例可以具有共同的基礎(chǔ)構(gòu)架,維護(hù)起來就非常容易。
4、編寫敏捷測試
測試工作非常耗時,因此請確保你的測試是有效的。好的測試應(yīng)該針對每個模塊的核心功能,而不是事無巨細(xì)挨個測試。
舉個例子來說,你沒有理由來為Java Bean的Setter和getter方法來單獨編寫測試代碼,因為它們肯定會測試中被涉及到的。
相反,你應(yīng)該針對軟件系統(tǒng)的行為來編寫測試代碼。你不需要面面俱到;為現(xiàn)在想到的東西先創(chuàng)建測試,然后想到別的再回來增加更多測試。
5、為每一個測試創(chuàng)造干凈的環(huán)境
軟件工程師們總是很在乎效率,視效率如生命。因此如果你告訴他們,每一個測試需要單獨創(chuàng)建的話,他們會認(rèn)為比較浪費時間。
然而這一點是非常重要的,如果一個測試因為使用了一些其它測試的老數(shù)據(jù)而失敗,顯然這是你最不想發(fā)生的事情。因此請確保每一個測試被正確的創(chuàng)建,而不要擔(dān)心所謂的效率。
如果你所有的測試具有一個共同的環(huán)境,它并不隨著測試的運行而改變,那么你可以在你的測試基類中增加一個靜態(tài)的創(chuàng)建區(qū)。
6、使用模擬對象(Mock Objects)進(jìn)行有效測試
建立測試并不總是一件簡單的事情,在有些時候你的第一感覺往往是沒有辦法進(jìn)行測試。
舉個例子來說,如果在你的程序中使用了Amazon Web服務(wù),如何在不影響真實系統(tǒng)的前提下在測試中模仿它呢?
其實有很多方法可用。你可以創(chuàng)建一些虛假的數(shù)據(jù),然后在測試中使用它們。在有用戶的系統(tǒng)中,可以創(chuàng)建一系列專門用于測試的賬號。
對一個生產(chǎn)系統(tǒng)進(jìn)行測試是一件非常危險的做法:如果發(fā)生點什么錯誤,你可能會誤刪真正的用戶數(shù)據(jù)。一個可行的辦法就是使用假數(shù)據(jù),又叫做模擬對象(stubs or mock objects)。
模擬對象實現(xiàn)了特定的接口,但是返回預(yù)先定義好的結(jié)果。舉個例子來說,你可以為Amazon S3創(chuàng)建一個模擬對象,它只從你的本地硬盤中讀取預(yù)先設(shè)置好的數(shù)據(jù),而不去使用真實服務(wù)數(shù)據(jù)。
在對具有很多構(gòu)件的復(fù)雜系統(tǒng)進(jìn)行測試的時候,模擬對象是非常有用的。在JavaScript中有幾個框架可以非常方便的創(chuàng)建模擬對象,其中最出名的是JMock。
7、代碼重構(gòu)時也對測試代碼進(jìn)行重構(gòu)
你只有對測試工作真正投入,它才會發(fā)揮更大的作用。你不僅僅要編寫測試,你還需要確保它們及時更新。當(dāng)為一個組件增加一個新方法時,你需要同時增加一個或更多的相應(yīng)測試。同樣,如果你要刪除無用代碼的話,也要刪除已經(jīng)不再有用的測試。
在進(jìn)行大量重構(gòu)工作的時候單元測試尤其有用。利用重構(gòu)可以大大提高我們的編程效率,不過對代碼完成了重構(gòu)后,別忘了也對測試進(jìn)行相應(yīng)的修改,并要重新運行一下所有相關(guān)測試,以確保在進(jìn)行程序改動的時候沒有出現(xiàn)錯誤。
8、針對發(fā)現(xiàn)的程序漏洞編寫測試
在減少漏洞方面,單元測試是一個非常有用的武器。當(dāng)你發(fā)現(xiàn)了代碼中的問題后,在對其進(jìn)行修復(fù)以前,先編寫一個測試來讓這個問題可以在測試的時候暴露出來。這樣,如果這個問題在其它地方重新出現(xiàn)的話,你就可以在測試的時候輕松發(fā)現(xiàn)它們。
這是一個非常有用的經(jīng)驗,因為你不可能總是馬上就編寫出能發(fā)現(xiàn)所有問題的測試代碼。當(dāng)你增加了針對某種漏洞的測試后,實際上你的測試代碼距離完美又靠近了一步。
9、使用單元測試來確保性能
除了確保代碼的正確性之外,單元測試還可以幫助我們確保代碼的性能不會隨時間而下降。在很多系統(tǒng)中存在這個問題,隨著系統(tǒng)開發(fā)工作的繼續(xù)進(jìn)行,被加入了越來越多的代碼,會變得越來越慢。
要想編寫性能測試,你需要在你的測試基類中增加start和stop功能函數(shù)。在單元測試階段就開始考慮性能測試是非常必要的,這樣在代碼發(fā)生改變或者對代碼進(jìn)行了優(yōu)化時就能看出這些改變是否會影響到性能,能盡早的發(fā)現(xiàn)代碼變更對性能的影響。
一個系統(tǒng)的性能如果拆分來看,可以看成若干單元性能的一個有機結(jié)合,單元的性能勢必也會對整個系統(tǒng)的性能產(chǎn)生影響,所以盡早考慮性能測試是很必要的,當(dāng)然這需要很好的分析和設(shè)計了。
10、為并發(fā)代碼創(chuàng)建測試
并發(fā)代碼通常不容易控制,通常是許多漏洞的源頭。因此對它們進(jìn)行單元測試是非常重要的。實現(xiàn)方法是使用一個休眠和鎖定機制。如果你需要等待一個特定的系統(tǒng)狀態(tài),你可以在你的測試中 使用休眠調(diào)用功能。
盡管這并不是一個完全正確的解決辦法,但是在很多情況下它已經(jīng)夠用了。
如果要在一個更加復(fù)雜的場景中模擬并發(fā)性,你需要圍繞你測試的對象進(jìn)行鎖的傳遞。這樣你可以模擬并發(fā)系統(tǒng),不過它是線性的。
11、連續(xù)運行測試
軟件測試的要點就是多多運行它們。尤其在大型開發(fā)團隊中,常常有數(shù)十個開發(fā)者共同修改同一個程序,這種情況進(jìn)行連續(xù)的單元測試是非常重要的。
你可以每隔幾個小時運行一次測試,也可以每次當(dāng)有代碼加入的時候運行測試,或者每天運行一次測試。根據(jù)自己的實際情況決定哪一種方法最適合你的項目,然后讓測試自動連續(xù)運行。
12、享受測試的樂趣
這可能是軟件測試中最重要的技巧。當(dāng)我第一次發(fā)現(xiàn)單元測試的時候,我懷疑它簡直就是一件多此一舉的事情。但是我還是嘗試著接受了它,因為我信賴的一些優(yōu)秀程序員告訴我它非常有用。
技術(shù)大師Martin Fowler曾表示,單元測試能夠使你更快地完成工作。無數(shù)次的實踐已經(jīng)證明這一點。你的時間越是緊張,就越是要寫單元測試,它看上去慢,但實際上能夠幫助你更快、更舒服地達(dá)到目標(biāo)。
單元測試可以讓你的大腦處于一種完全不同于編程時的狀態(tài)。為一個指定的構(gòu)件設(shè)計一個簡單又正確的測試代碼是一件非常有意思的事情。
一旦你開始編寫測試,你會發(fā)現(xiàn)你已經(jīng)離不開它。為了讓測試變得更有趣,你可以與別人進(jìn)行結(jié)對編程(pair programming)。無論你是與同伴一起編寫測試,還是互相為對方編寫測試,都可以體驗到其中的樂趣。最終你將會非常高興的認(rèn)為你的系統(tǒng)可以真正運行,因為它通過了你的測試。