單元測試本質:面向邏輯塊

    單元測試是最先階段的軟件測試,面對的目標最小,能夠綜合使用黑盒測試方法和白盒測試方法,按理說,單元測試用例的設計應該是最簡單的,但實際上,單元測試用例的設計常讓人感受無從下手,這是什麼緣由?是代碼真的不具備「可測性」嗎?仍是測試思路和方法不對?正確的測試思路和方法是什麼?單元測試工具應該具有什麼樣的功能,才能支持快速地構建測試用例?

    大道至簡,意思是掌握了事物的本質,事情就會變得很簡單。反之,若是事情很複雜很麻煩,每每表示沒有抓住本質。

   
單元測試的本質是什麼?首先要看單元測試的目標是什麼。單元測試檢測代碼功能邏輯,實現高質高效的編程。只要真正作過單元測試的工程師都知道,單元測試要作的,能作的,就是檢測代碼的功能邏輯,扯上其餘東西是沒有任何意義的。既然單元測試是對代碼功能邏輯的檢測,那麼,測試用例要作的,就是針對代碼的功能邏輯,設定其輸入,並判斷其輸出是否符合預期,從而檢測功能邏輯的正確性。功能邏輯是由什麼實現的?邏輯塊。因此,單元測試的本質,是面向邏輯塊,單元測試用例的本質,是邏輯塊的輸入輸出,也就是設定邏輯塊的各類可能輸入,及對應的預期輸出。

   
一旦咱們把目光轉向邏輯塊,全部的事情就會變得簡單。來看一個典型示例。「典型」的意思是,若是這個代碼不會測,實際項目也就不會測,由於一樣的測試問題會大量存在;反過來,若是這個代碼能夠測得很好很快,那麼實際項目的測試就基本上沒有問題,由於已經掌握了正確的測試思路和測試方法。這個代碼的功能是,取得職位列表,將職位標題拼成短信併發送給用戶。參數是一個數據流,包含用戶的手機號和想要什麼類型的職位等信息,程序從數據庫裏讀取對應的職位列表和一個映射表,映射表用來檢查哪些職位已經發送給用戶,而後把職位的標題拼成短信而且發送給用戶。代碼使用C++編寫,但它所表達的測試問題和測試思想,則是通用的。html

   
   

    請想想,這個代碼的測試思路是什麼?也就是說,哪些變量要設置輸入,哪些變量要判斷輸出?若是按照傳統的方法,輸入是參數,輸出是返回值,那基本無法測。可是若是面向邏輯塊(上面的代碼分爲兩張圖片,第二張圖片實現函數的功能邏輯,也就是咱們要測的邏輯塊),馬上就有了思路:輸入是鏈表對象objList和映射表對象map裏的數據,輸出是拼接出來的字符串,在兩個地方須要判斷它的值。

   
具有了面向邏輯塊的測試思路,就能夠將「可測性」這個詞扔進垃圾桶了,除非代碼真的糟糕得太過度,不然都不難測。至於代碼之間的耦合,那是再正常不過的事情,代碼反映了客觀事物,客觀事物自己就是互相關聯的,代碼能沒有耦合?若是一個函數有多個邏輯塊,一樣很簡單,各個邏輯塊分別測試就是了。

   
面向邏輯塊,測試用例的數據也很簡單,由於邏輯計算涉及到的數據每每不多,且通常是基本類型。上面的示例,數據算是比較麻煩的,多數代碼的測試數據量都少於這個示例。雖然這個示例的數據看起來很嚇人,又有鏈表,又有映射表,但實際上,邏輯計算中涉及到的輸入就是一些標題(title),字符串而已,也就是說,咱們要加入到鏈表和映射表中的對象指針,無論它的類型多複雜,每一個對象只須要設定標題(title)就OK了。至於輸出,也不過是字符串。總之,對於這個示例,用例的輸入是一系列字符串,輸出也是一些字符串。

   
傳統單元測試思想,是面向函數,即用例由函數的輸入輸出構成,這在一些特例中沒有問題,例如三角形函數、排序函數之類最底層函數。這些函數,只有一個邏輯塊,且邏輯塊的輸入輸出,與函數的輸入輸出徹底一致,固然可使用函數的輸入輸出來構建測試用例。傳統的單元測試用例設計技術,都拿這些特例做爲基礎,當面對含有耦合關係的代碼時,反而做爲特例來處理,要使用編寫樁代碼、設置模擬對象之類的麻煩方法來解決(實際上不少時候解決不了問題,例如,前面示例中的輸出怎麼辦?)。實際項目中,代碼存在耦合關係是常態,徹底沒有耦合的代碼反而不多。這種拿特例當常態,拿常態當特例的方法,在本質上是錯誤的,所以必然很麻煩,從根本上形成了單元測試難度大、成本高。總之,面向函數來設計單元測試用例,測試將很困難,至於主張單元測試要面向對象、面向模塊,那純粹是胡扯。

   
有了面向邏輯塊的測試思想,測試思路是很簡單了,可是,如何設置邏輯塊的輸入值和輸出值呢?邏輯塊的輸入,除了參數、成員變量之類的常規變量,還包括底層輸入,即調用底層函數得到的輸入,如前面示例中的鏈表和映射表對象中的數據;不少時候,還包括局部輸入,即在被測試代碼執行過程當中對某些變量的實時賦值,如局部靜態輸入、中斷輸入、界面輸入等。邏輯塊的輸出,除了返回值、成員變量之類的常規變量,還包括局部輸出,即被測試代碼執行過程當中對某些變量的實時判斷,如前面示例中直接發送出去的短信須要在發送前實時判斷。這些問題,偏偏代表了單元測試的另外一個簡單:選擇工具很簡單。若是工具不能直接地、方便地設定邏輯塊的輸入輸出,那基本上無法用,或者成本很高(至少十倍以上),所以,選擇工具的最主要指標,就是可否直接地、方便地設定邏輯塊的輸入輸出。C/C++單元測試工具Visual Unit 4能夠經過在表格中填寫數據,直接設定邏輯塊的輸入輸出,例如前面的示例,使用Visual Unit 4,只要點點鼠標,在表格中填寫一些字符串,就能夠構建出鏈表和映射表中的數據,以及判斷所拼接的短信是否正確。


   
也許有人認爲,對於前面的示例,若是面向函數,經過設定參數來得到鏈表和映射表的數據,也能夠達到一樣的測試效果,甚至能夠同時檢測代碼所調用的其餘函數,例如用於解析用戶信息的GetUserInfo ()函數,用於從數據庫讀取職位列表的GetJobList()函數。這種想法是徹底錯誤的,白白浪費時間和精力,爲何?
    一、這些函數可能尚未實現,這在並行開發中很常見;
    二、這些函數或者它們所依賴的函數在測試時可能被隔離,這在大型項目中很常見;
    三、相關設備在測試時可能不存在,例如,單元測試通常不鏈接數據庫;
    四、相關設備沒法返回測試須要的數據,例如,一個取環境溫度的底層函數,老是返回固定值;
    五、即便以上問題都不存在,經過設置參數來間接得到邏輯塊的輸入也可能很是困難,例如前面的示例,必須熟悉通信協議,瞭解GetUserInfo ()函數的工做過程,並在參數中填寫正確的數據流,且數據庫裏有合適的數據,纔可能得到鏈表和映射表中的數據。

    面向邏輯塊,則徹底不須要考慮這些問題,不管多大的項目,不管多少人並行開發,均可以在開始編寫代碼時,就作到邊開發邊測試。至於底層函數,誰家的孩子誰抱,應該由編寫者直接進行測試,這樣才能全面地檢測它的功能邏輯。


   
總之,單元測試應該面向邏輯塊,只有這樣,才能迅速產生測試思路,才能快速構建用例數據,才能檢測功能邏輯的方方面面,不留死角,而判斷一個單元測試工具是否能夠高效地應用於實際項目,最主要的指標是可否直接地、方便地設置邏輯塊的輸入輸出。
數據庫

相關文章
相關標籤/搜索