談“測試驅動的開發”

Ant發表於2020-04-06

原文連結:http://blog.sina.com.cn/s/blog_5d90e82f0101kfnd.html

現在的很多公司,包括 Google 和我現在的公司 Coverity,都喜歡一種“測試驅動的開發”(test-driven development)。它的原理是,在寫程式的時候同時寫上自動化的“單元測試”(unit test)。在程式碼修改之後,這些測試可以批量的被執行,這樣就可以避免不應該出現的錯誤。

  這不是一個壞主意。我在 Kent 的編譯器課程上也使用了很多測試。它們在編譯器的開發中是不可缺少的。編譯器是一種極其精密的程式,微小的改動都可能帶來重大的錯誤。所以編譯器的專案一般都含有大量的測試。

  但如果使用不當,測試卻會大幅度的降低開發效率。當我給 Google 開發 Python 靜態分析的時候,我幾乎沒有使用任何測試。雖然組裡的成員催我寫測試,但是我卻知道那隻會降低我的開發效率。因為這個程式在幾個星期的過程中,被我推翻重來了好幾次。要是我一開頭就寫上測試,這些測試就會礙手礙腳,阻礙我大幅度的修改程式碼。

  最後的結果是,我在 12 個星期之內,寫出了 Google 一個小組的人需要幾年才做得出來的東西。現在這個東西里面的技術,仍然處於世界領先地位。就連 Coverity 這種專門做靜態分析軟體的公司,程式碼質量也無法與之相比。按照他們的開發方式,要想在 12 個星期之內做出這個東西,是完全不可能的事情。

  測試的另一個副作用是,它讓很多人對測試有一種盲目的依賴心理。改了程式之後,把測試跑一遍沒出錯,就以為自己的程式碼是正確的。可是測試其實並不能保證程式碼的正確,即使完全“覆蓋”了也是一樣。覆蓋只是說你的程式碼被測試碰到過了,可是它在什麼條件下碰到的卻沒法判斷。如果實際的條件跟測試時的條件不同,那麼實際執行中仍然會出問題。唯一能可靠的確保程式碼正確的方法是使用嚴密的邏輯推理,證明它的正確。

  很多人寫程式只是憑現象來判斷,而不能精密的分析程式的邏輯,所以他們修改程式經常“治標不治本”。如果程式出問題了,他們的辦法是看看哪裡錯了,也不怎麼理解,就改一下讓它不再出錯,最多再把所有測試跑一遍。或者再加上一些新的測試,用以保證這個地方下次不再出問題。

  這種做法的結果是,程式裡出現大量的“特殊情況”和“補丁”。把一個“蟲子”按下去,另一個蟲子又冒出來。忙活來忙活去,最後仍然不能讓程式滿足“所有情況”。其實能夠“覆蓋所有情況”的程式,往往比能夠“覆蓋特殊情況”的程式簡單很多。這是一個很奇怪的事情:能做的事越多,程式碼量卻越少。也許這就叫做程式的“美”。

  美的程式不可能從修修補補中來。它必須完美的符合事物的本質,否則就會出現上面的情況,有許許多多無法修補的特例。程式設計師跟畫家其實差不多,畫家如果一天到頭蹲在家裡,肯定什麼好東西也畫不出來。程式設計師也一樣,蹲在家裡面對電腦,其實很難寫出什麼好的程式碼。你必須出去觀察事物,尋找“靈感”,而不只是修改程式碼。在修改程式碼的時候,你必須用“心靈之眼”看見程式碼背後的事物。這也是為什麼很多高明的程式設計師不怎麼用偵錯程式(debugger)的原因。他們只是用眼睛看著程式碼,然後閉上眼,腦海裡浮現出其中資訊的流動,所以他們經常一動手就能改到正確的地方。

相關文章