電腦科學和Python程式設計導論(六) 測試與除錯

Datawhale發表於2018-07-27

基本概念

測試指通過執行程式以確定它是否按照預期工作。

除錯則指修復已知的未按預期工作的程式。

測試和除錯的關鍵就是將程式分解成獨立的部件,可以在不受其他部件影響的情況下實現、測試和除錯。

關於測試,最重要的是清楚它的目的是證明錯誤的存在,而不是證明程式沒有錯誤。

測試的關鍵就是找到極有可能產生錯誤答案的一組輸入,可以稱之為測試套件

找到測試套件的關鍵是,對所有可能的輸入空間進行分割槽,將其劃分為對程式正確性提供相同資訊的多個子集,然後構建測試套件,使其包含來自每個分割槽的至少一個輸入。

如果使用來自每個子集的至少一個值對函式實現進行測試,就非常有可能暴露可能存在的錯誤。

基於程式碼探索路徑的啟發式方法稱為白盒測試

基於規範探索路徑的啟發式方法稱為黑盒測試

如果一個白盒測試套件可以測試程式中所有潛在路徑,那我們就可以認為它是路徑完備的。一般來說,路徑完備不可能達成,因為這取決於程式中迴圈的次數和遞迴的深度。

白盒測試提供的一些經驗準則

 測試所有if語句的所有分支。
 必須測試每個except子句。
 對於每個for迴圈,需要以下測試用例:

  1. 未進入迴圈(例如,如果使用迴圈遍歷列表中的所有元素,則必須測試空列表);
  2. 迴圈體只被執行一次;
  3. 迴圈體被執行多於一次;

 對於每個while迴圈:

  1. 包括上面for迴圈中的所有用例;
  2. 還要包括對應於所有跳出迴圈的方式的測試用例。例如,對於以while len(L) > 0 and
    not L[i] == e開始的迴圈,測試用例應該包括因為len(L)不大於0和因為L[i] == e
    而跳出迴圈的情況。

 對於遞迴函式,測試用例應該包括函式沒有遞迴呼叫就返回、只執行一次遞迴呼叫和執
行多次遞迴呼叫的情況。

測試一般分為兩個階段。第一個階段稱為單元測試,第二個階段稱為整合測試。

第一個階段稱為單元測試。在這個階段中,測試者構建並執行測試,
用來確定程式碼的每個獨立單元(例如,函式)是否正常工作

第二個階段稱為整合測試,用來確
定整個程式能否按預期執行。

在工業界,測試過程通常是高度自動化的。測試者不會坐在終端前面手動輸入用例並檢查輸出。他們會使用測試驅動程式

顯性錯誤有明顯的表現,如程式崩潰或執行時間異常長(可能永不停止)

隱性錯誤沒有明顯的表現,程式會正常結束,不出任何問題——除了給出一個錯誤答案

持續性錯誤在程式每次使用相同的輸入執行時都會發生

間歇性錯誤僅在某些時候出現,即使程式使用相同輸入並在相同條件下執行

優秀的程式設計師編寫程式時,會盡量使程式錯誤是顯性的和持續性的,這種程式設計方式通常稱為防禦性程式設計

多數程式設計師認為最重要的除錯工具是print語句

如果將除錯看作一個搜尋過程,那麼每次實驗就要盡力縮減搜尋空間。

縮減搜尋空間的一種方法是,設計一個實驗,確定程式碼的一個具體區域是否是造成某個問題的原因。另外一種縮減搜尋空間的方法是,減少導致錯誤出現所需的測試資料量。

系統地縮減搜尋空間,最好的方法是執行二分查詢。先找出程式碼中間點,然後設計一個實驗,確定是否因為中間點前面存在問題才導致程式出現這種症狀

除錯遇到困難時,我們該怎麼做呢?

 排除常見錯誤。例如,看看你是否犯了以下錯誤:

  1. 以錯誤的順序向函式傳遞實參;
  2. 拼錯一個名稱,如將大寫字母寫成小寫;
  3. 變數重新初始化失敗;
  4. 檢驗兩個浮點數是否相等(==),而不是近似相等(請記住,浮點數的運算與學校裡學
    的運算不一樣);
  5. 在應該檢驗物件相等(如id(L1) == id(L2))的時候,檢驗值相等(例如,使用表示式 L1 == L2比較兩個列表);
  6. 忘記了一些內建函式具有副作用;
  7. 忘記使用()將對function型別物件的引用轉換為函式呼叫;
  8. 意外地建立了一個別名;
  9. 其他一些你常犯的錯誤。

 不要問自己為什麼程式沒有按照你的想法去做,而要問自己程式為什麼像現在這樣做。後者應該更容易回答,要想弄清楚如何修復程式,這可能是一個很好的開始。
 記住,錯誤可能不在你認為會出錯的地方。如果在那裡,你早就應該發現它了。確定錯誤位置的一種實用方法是,看看那些你認為不會出錯的地方。
 試著向其他人解釋程式的問題。每個人都會有盲點。經常有這樣的情況,試圖向別人解釋問題的時候,你會突然發現自己忽略的地方。向其他人解釋為什麼程式中某個地方不會出現錯誤是個很好的選擇。
 不要盲目相信任何書面上的東西。特別是,不要相信文件。程式碼行為可能與註釋不一樣。
 暫停除錯,開始編寫文件。這會幫助你從不同視角接近問題所在。
 出去散散步,明天接著做。這可能意味著與你堅持工作相比,修復問題的時間要晚一些,但花費的總時間會大大減少。也就是說,我們使用時間上的一點延遲換取了效率上的大幅提升。(同學們,開始習題集中的程式設計練習吧,寧早勿晚,這是個絕好的理由!)

我們的目標不是修復一個錯誤,而是快速有效地得到一個沒有錯誤的程式。你應該捫心自問,這個錯誤能夠解釋所有觀測到的症狀,還是隻是冰山一角。如果是後者,最好將對這個錯誤的處理與其他修改結合考慮。

相關文章