測試驅動開發 —— 一種真正的工程化開發實踐 (轉載自孫鳴、鄧輝)

   自從軟件危機的概念被提出以來,人們就在不斷地探索解決之道。期間,這些探索者們從其他如硬件、建築等相對成熟的行業借鑑了不少經驗和知識,希望能夠以工程化的方法解決軟件領域所面對的難題,並提出了“軟件工程”這樣一個知識框架用以指導實踐。但是,幾十年過去了,結果表明,“軟件工程”爲我們帶來的對軟件開發本身反思方面的作用要遠遠大於解決軟件危機方面的作用。

    經歷了這麼長時間不那麼成功的“軟件工程”經歷的人們不禁要問:爲何在其他領域屢獲成功的工程化經驗,對於軟件領域就不起作用了呢?Richard P. Gabriel在其“Patterns Of Software”一書中的一句話道出了最根本的原因:“…… 我們談論了太多的關於軟件的東西,對於軟件這個東西本身,關注得太少了……”。確實是這樣,我們從其他領域借鑑了太多的形式的東西,而對於軟件以及軟件開發活動本身的規律的認識上卻思考不多。而工程化的根本就在於契合事物本身的規律。

    關於軟件本身的特點和規律,Jack W. Reeves在其著名的論文“What Is Software Design?”中有深刻的論述。在此,僅列出幾個重要的結論:
? * 軟件的設計成本非常低,因此很容易變得極其複雜
? * 軟件的構建成本基本爲零,因此先構建出來然後驗證是一種經濟的做法
? * 軟件不受任何物理規律的約束,因此可能出現的故障情況比其他領域要高得多。
? * 軟件是非常精確的,一個bit的錯誤都會導致整個系統的崩潰。
    這些規律對軟件開發有着根本性的影響,主要表現在:需求範圍的限定、設計成本的控制,需求滿足性的驗證以及軟件產品本身的健康演進。

    在我們實際的軟件開發中,最令人頭疼的問題莫過於兩點:1、時間有限,要實現的功能太多;2、bug層出不窮,好像永遠也改不完。一般來講,造成這兩個問題的根源都是由於違背了上述的軟件本身的規律。下面我們來簡單分析一下。

    我們的需求分析結果是以軟件需求規格說明書的形式交付的,描述的語言是自然語言。自然語言往往是含糊地。雖然對人來講覺得可能已經很清楚了,但是對於真正去執行的計算機來說,精確度遠遠不夠。因此,需求和實現之間存在着許多“不那麼明確”的地方。由於軟件的設計成本很低,因此,“負責任”開發人員在設計和實現時,會不經意地把這些不明確的地方最大化,造成需求和軟件開發成本的隱式膨脹。如果我們要求每增加一個設計元素,那麼設計開發人員就要去爬十層樓,相信我們的軟件會減掉不少“贅肉”:)

    此外,大家想想,我們的軟件中的bug都是在什麼階段大量涌現的?沒錯,是聯調階段。正是由於軟件的構建成本很低,因此,我們才得以在前期不那麼有紀律地進行編碼,然後把軟件構建出來進行調試,驗證。把軟件先構建出來再驗證本身沒什麼問題,問題在於由於軟件不受任何物理規律約束,並且非常精確,因此任何一丁點的修改都可能造成難以預料的嚴重後果。而我們也缺乏一種有效的檢查修改所造成影響的手段。測試部門的人工測試或者基於界面的robot測試非常的低效且容易遺漏。

    那麼我們怎麼做才能符合軟件本身的規律,從而在最大程度上避免上述問題呢?我們需要用和實現語言同樣精確的語言來描述需求;我們需要用和實現語言同樣精確的語言來描述驗收條件;我們需要足夠細粒度的檢查點來固定我們的軟件;這些檢查點的運行要足夠快、成本足夠低以便於我們可以經常性的運行它們。這意味着什麼呢?這意味着開發人員在編寫實現代碼前,要先用同樣的語言編寫測試,用測試代碼來表達需求和驗收條件,用測試來驅動整個的開發過程。這樣做能有效地解決上述問題嗎?我們來分析一下。

    軟件是精確地,容不得半點含糊。因此用編程語言描述需求可以避免那些似是而非的情況,如果覺得自己很清楚一個需求,那麼請用編程語言寫出一個測試用例,精確地表達需求場景和驗收條件,如果寫不出來,那就表明還沒有搞清楚。此外,有了這個可以用編程語言描述的驗收條件,就有了準確的判斷完成的標準。一切以完成驗收條件爲目標,這樣也就避免了因爲沒有精確目標而隨手編寫多餘的代碼。

    另一方面,如果用測試來驅動我們的開發,那麼每一個測試用例就充當了用以固定軟件行爲的檢查點的角色。隨着功能的不斷增加,這些測試用例不斷地累積,形成了一張堅固的安全網絡。每當我們增加新功能時;每當我們更改bug時;每當我們重構代碼時;我們都可以即時地運行這些測試,然後就可以立即得到反饋。有了這些檢查點,一旦發現問題,可以很快地進行定位:問題一定出在上一次正常運行和這一次故障運行之間的修改上面,我們的測試用例的粒度越小,運行得越頻繁,問題定位起來就越容易。

    如果用測試來驅動開發,那麼我們寫出的代碼將天生就具有可測試性。而可測試性是好的設計的重要標誌。我們的實現代碼將完全可以脫離預先設定的環境進行測試,測試的粒度完全可以根據需要進行調整。和完整的系統測試相比,這樣的測試成本更低、運行更快、更靈活,更加可控,它們也是持續集成的重要基石。

    軟件開發就像是攀巖,既充滿樂趣,又深具挑戰,有時甚至危險重重。正如攀巖時最安全有效的方法是保證我們的四肢中每次僅移動有一個一樣,我們在軟件開發中也要遵守這樣的紀律。在軟件開發活動中,我們的四肢是什麼呢?它們分別是:編寫測試代碼;編寫實現代碼;修改測試代碼;修改實現代碼。我們要非常清楚地知道我們正在移動得是四肢中的哪一個,並嚴格按照紀律執行。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章