引入單元測試是很簡單的,因為它本身就充滿了樂趣。然而在項目交付的時候,我們給客戶和最終用戶的仍然是產品代碼,而不包含單元測試的代碼;因此,我們必須對單元測試的目的有個充分的認識。首先也是最重要的,使用單元測試是為了使你的工作——以及你隊友的工作——完成得更加輕松。
n 它的行為和我的期望一致嗎?
最根本的,你需要回答下面這個問題:“這段代碼達到我的目的了嗎?”也許就需求而言,代碼所做的是錯誤的事情,但那是另外一個問題了。你要的是代碼向你證明它所做的就是你所期望的。
n 它的行為一直和我的期望一致嗎?
許多開發者說他們只編寫一個測試。也就是讓所有代碼從頭到尾跑一次,只測試代碼的一條正確執行路徑,只要這樣走一遍下來沒有問題,測試也就算是完成了。
但是,現實生活當然不會這么事事順心,事情也不總是那么美好:代碼會拋出異常,硬盤會沒有剩余空間,網絡會掉線,緩沖區會溢出等——而我們寫的代碼也會出現bug。這就是軟件開發的“工程”部分。就“工程”而言,土木工程師在設計一座橋梁的時候,必須考慮橋梁的負載、強風的影響、地震、洪水等等。電子工程師要考慮頻率漂移、電壓尖峰、噪音,甚至這些同時出現時所帶來的問題。
你不能這樣來測試一座橋梁:在風和日麗的某一天,僅讓一輛車順利地開過這座橋。顯然,這種測試對于橋梁測試來說是遠遠不夠的。類似地,在測試某段代碼的行為是否和你的期望一致時,你需要確認:在任何情況下,這段代碼是否都和你的期望一致;譬如在風很大、參數很可疑、硬盤沒有剩余空間、網絡掉線的時候。
n 我可以依賴單元測試嗎?
不能依賴的代碼是沒有多大用處的。但更糟糕的是,那些你自認為可以信賴的代碼(但是結果證明這些代碼是有bug 的)有時候也會讓你花很多時間在跟蹤和調試上面。顯然,幾乎沒有項目可以允許你在這上面浪費太多的時間,因此無論如何,你都要避免這種“前進一步,后退兩步”的開發方法。也就是說,要讓開發過程保持穩定的步伐前進。
沒人能夠寫出完美無缺的代碼;但是這并沒有關系——只要你知道問題的所在就足夠了。許多大型軟件項目的失敗,諸如只能把壞了的太空船擱淺在遙遠的行星,或者在飛行的途中就爆炸了,都能通過認知軟件的限制來避免。例如,Arianne 5 號火箭軟件重用了來自于之前一個火箭項目的一個程序庫,而這個程序庫并不能處理新火箭的飛行高度(比原來火箭要高)(引入單元測試是很簡單的,因為它本身就充滿了樂趣。然而在項目交付的時候,我們給客戶和最終用戶的仍然是產品代碼,而不包含單元測試的代碼;因此,我們必須對單元測試的目的有個充分的認識。首先也是最重要的,使用單元測試是為了使你的工作——以及你隊友的工作——完成得更加輕松。) ,從而在起飛40 秒之后就發生了爆炸,導致5 億美元的損失。
顯然,我們希望能夠依賴于所編寫的代碼,并且清楚地知道這些代碼的功能和約束。
例如,假設你寫了一個反轉數值序列的方法。在測試的過程中,你也許會傳一個空序列給這個程序——但導致了程序崩潰。實際上,程序并沒有要求該程序必須能夠接收一個空序列,因此你可以只在方法的注釋中說明這個約束:如果傳遞一個空序列給這個方法,那么這個方法將會拋出一個異!,F在你馬上就知道了該代碼的約束,從而也就不需要用其他很麻煩的方法來解決這個問題(因為在某些地點要解決這個問題并不方便,比如在高空大氣層中)。