2.1 可測性問題詳解(2)
接下來我們討論重點問題:覆蓋輸入。一個函數,輸入會有哪些呢?輸入包括兩方面:外部輸入,內部輸入。外部輸入容易理解,就是函數外部可以設定的輸入,包括參數,全域變數,成員變數。
關鍵是內部輸入。因為很少有文獻討論內部輸入,對很多人來說,內部輸入可能是一個陌生的詞,我們首先來看看內部輸入是什麼。一個函數,對於調用底層函數獲得的資料,是如何處理的呢?跟參數一樣,也是分類處理。所以,測試時也要分類檢測,這與參數沒什麼區別。這就是內部輸入。
內部輸入有幾種情形?一共有六種:自然輸入、不可控、失真、難於初始化、靜態輸入,中斷輸入。
自然輸入
自然輸入就是調用底層函數的實際代碼,獲得自然的計算結果。底層函數必須存在,可控,並且正確。對於自然輸入,測試時並不是什麼也不用做。為了檢測程式是否對底層函數的各種可能輸出做合適的判斷和處理,需要讓底層函數輸出合適的資料,這就要通過設定合適的參數等資料來間接控制底層函數的輸出。有時候,這個工作是很困難的,這就是難以初始化,後面有專門介紹。
不可控
底層函數還是調用實際代碼,但是底層函數的輸出不符合測試需求。在這個例子中,底層函數的功能是取得環境溫度,我們要檢測程式是否對各種環境溫度做了合適的處理,但是真實的環境溫度不可能即時大幅變化,這就是不可控。不可控在單元測試當中是相當常見的,例如底層函數返回一個隨機數、也是不可控,底層函數是用來串連網路的,可能無法控制它的各種狀態,這些都是不可控。
失真
底層函數調用的是樁代碼。樁代碼當然不能實現原有代碼的功能,這就是失真。這個例子跟介紹不可控是同一個函數,不同的是,底層函數調用的是樁代碼,一般的樁代碼是什麼也不做的,底層函數的傳回值總是0,並且未輸出環境溫度,測試做不下去。失真是打樁造成的,是打樁的必然結果。可不可以通過修改樁代碼來解決失真呢?有時候是可以的,有時候不行,後面會有進一步的介紹。
剛才我們討論了失真。
難於初始化
在談到自然輸入時,我們討論過,為了使底層函數產生需要的測試輸出,需合設定合適的外部輸入,即通過設定外部輸入的方式來初始化底層函數,很多時候,這個工作是很困難的,例如,要使圓的面積等於100.00,半徑應該是多少?再如,為了使一個映射表在搜尋某個對象時能返回真,需要預先建立該對象並加入表中,很簡單的一個輸出,卻需要比較麻煩的初始化。實際工作中比這些例子更難的多得是。這些就是難於初始化。
靜態輸入
局部靜態變數在C代碼中,尤其是嵌入式代碼中相當常用。局部靜態變數與全域變數一樣,通常每個用例也需要設定不同初值。但在外部卻無法訪問,這也是一種內部輸入。
中斷輸入
中斷輸入常見於嵌入式項目。如果在被測程式運行過程中,系統有可能產生中斷,中斷可能調用某些代碼,並且可能造成全域變數的修改,而且這種修改又會影響程式的功能邏輯,那麼,這也是一種內部輸入,測試時也必須考慮。
前面介紹了內部輸入的六種情形,除了自然輸入外,其他五種都是必須解決的。
如果解決可測性問題呢?前面說過,試圖通過改進開發流程來解決可測性是不現實的。通過對可測性問題的具體分析,我想大家也已經瞭解,無論怎樣改進開發流程,最多隻能解決一小部分問題。例如內部輸入問題,多數都不是因為代碼寫得不夠好形成的。我們只能通過改進測試技術,使用合適的工具來解決。