事實上,我們每天都在做單元測試。當妳寫壹個函數的時候,妳總是要執行它,看看函數是否正常,有時候妳還要想辦法輸出壹些數據,比如彈出信息窗口。這也是壹個單元測試。老納稱這個單元測試為臨時單元測試。對於只進行臨時單元測試的軟件,對於代碼的測試是非常不完整的,很難覆蓋70%以上的代碼。未覆蓋的代碼可能會留下很多小錯誤,這些小錯誤也會相互影響。當bug暴露後,很難調試,大大增加了後期測試和維護的成本,降低了開發者的競爭力。可以說,充分的單元測試是提高軟件質量、降低開發成本的必由之路。
對於程序員來說,如果養成對自己的代碼進行單元測試的習慣,不僅可以寫出高質量的代碼,還可以提高自己的編程水平。
為了進行充分的單元測試,測試代碼應該專門編寫,並與產品代碼隔離。老納認為,更簡單的辦法是為產品工程建立相應的測試項目,為每個類建立相應的測試類,為每個功能建立測試函數(非常簡單的除外)。首先說壹下老納對幾個概念的看法。
壹般認為,在結構化程序時代,單元測試中提到的單元是指函數,而在當今面向對象時代,單元測試中提到的單元是指類。按照老納的做法,以班級為測試單元,復雜度高,可操作性差。所以我們還是主張以函數作為單元測試的測試單元,但是可以用壹個測試類來組織壹個類的所有測試函數。單元測試不要過分強調面向對象,因為本地代碼還是結構化的。單元測試工作量大,簡單實用高效才是硬道理。
有壹種觀點認為,從面向對象的角度來看,只測試類的接口(公共函數)而不測試其他函數是有意義的。但是,測試的目的是發現錯誤並最終調試它。因此,只要有很高的出錯可能性,就應該測試所有的函數,不管這個函數是否是私有的。對於C++,可以用壹個簡單的方法來區分要測試的函數:簡單的函數如數據讀寫函數寫在頭文件中(inline function),所有在源文件中編寫和實現的函數都要測試(構造函數和析構函數除外)。
考試是什麽時候?單元測試越早越好。多早?XP開發理論講究TDD,即測試驅動開發,先寫測試代碼,再開發。在實際工作中,不必過分強調先來後到,重要的是效率和舒適度。從老娜的經驗來看,先寫產品功能的框架,再寫測試功能,針對產品功能的功能寫測試用例,再寫產品功能的代碼,每個功能點運行測試,隨時補充測試用例。所謂先寫產品函數的框架,是指先寫函數的空實現,有返回值就隨便返回值,編譯通過後再寫測試代碼。這個時候函數名、參數表、返回類型都要確定,以後寫測試代碼的可能性就比較小了。
誰來測試?單元測試不同於其他測試。單元測試可以看作是編碼工作的壹部分,應該由程序員來完成。也就是說,經過單元測試的代碼就是成品代碼,提交產品代碼的同時要提交測試代碼。檢測部門可以進行壹定程度的審核。
關於存根代碼,老那認為單元測試要避免寫存根代碼。存根代碼是用來替換某些代碼的代碼。比如壹個產品函數或者壹個測試函數調用壹個未寫的函數,可以寫壹個存根函數來代替被調用的函數,存根代碼也用來實現測試隔離。采用自底向上的方法開發,先開發底層代碼並進行測試,可以避免編寫存根代碼。這種方法的優點是:減少工作量;測試上層功能時,也是對下層功能的間接測試;當下部函數被修改時,可以通過回歸測試來確認修改是否導致上部函數的錯誤。