基於Python的行為驅動開發實戰

發表於2015-04-15

行為驅動開發(Behavior-Driven Development,BDD)是一種卓越的開發模式。能幫助開發者養成日清日結的好習慣,從而避免甚至杜絕“最後一分鐘”的情況出現,因此對提高程式碼質量是大有裨益的。其與Gherkin語法相結合的測試結構及設計形式,使得對團隊的全部成員包括非技術人員都具有極好的易讀性。

所有程式碼都必須進行測試,這意味著上線時把系統瑕疵降到最低甚至為零。這需要與完整的測試套件相配,從整體把控軟體行為,使得檢測與維護都能有序進行。這就是BDD的魅力所在,難道不心動嗎?

什麼是BDD?

BDD的概念和理論源自TDD(測試驅動開發),類似於TDD的理論要點是在編碼前先寫好測試。不同點是除了使用單元測試進行細粒度化測試,還使用接受測試(acceptance tests)貫穿程式始末。接下來我們會結合Lettuce測試框架進行講解。

BDD過程可簡單概括為:

  • 編寫一個缺陷接受測試
  • 編寫一個缺陷單元測試
  • 使單元測試通過
  • 重構
  • 使接受測試通過

在每個功能裡,如有需要重複上述步驟。

敏捷開發中的BDD

在敏捷開發中,BDD更是如魚得水。

如果專案的新功能和新需求每隔一、兩個星期就發生變更,那麼該團隊需要配合進行快節奏的測試和編碼工作。Python中的接受和單元測試可以幫助實現該目標。

接受測試為人熟知的是使用了一個英文格式的“特性”描述檔案,內容是含有的測試以及個別測試。這樣做的好處是使整個專案團隊都參與其中,除了開發者,還有管理者與商業分析者等不參與實際測試過程的非技術成員。

特性檔案的編寫遵循全員可讀的規則,使技術和非技術成員都能清楚理解與接收。如果只包含單元測試,那麼有可能會導致需求分析不全面或不能達成共識。接受測試的最大優點是適用性強,不論專案規模大小都能運用自如。

Gherkin語法

通常會使用Gherkin來編寫接受測試,Gherkin來自Cucumber框架,由Ruby語言所編寫。Gherkin語法十分簡單,在Lettuce Python中主要使用以下8點來進行特性和測試的定義:

  • Given假設
  • When時間
  • Then下一步
  • And與
  • Feature特性:
  • Background背景:
  • Scenario Outline場合大綱:

安裝

使用Python常用的pip install語句就可完成Lettuce包的安裝:

$ lettuce /path/to/example.feature用於執行測試。可以每次只執行一個測試檔案,或者是提交目錄名來執行目錄下的所有檔案。

為了使測試的編寫和使用更加容易,我們建議把nosetests也安裝好:

特性檔案

特性檔案由英語寫成,內容是測試所覆蓋的程式範圍。此外還包括測試的建立任務。換言之,你除了需要編寫測試,還得規範自己就程式的方方面面編寫出良好的文件。這樣做的好處是使自己對程式碼上下都心中有數,明確下一步做什麼。隨著專案規模的擴大,文件的重要性會逐步顯現;例如重新回顧某個功能或對某個呼叫API進行回溯等等。

接下來會結合TDD中的一個例項建立一個特性檔案。該例項是一個由Python寫成的簡易計算器,同時會演示接受測試的基本寫法。目錄構成的建議是建立兩個資料夾,一個是app,用於放置程式碼檔案如calculator.py;另一個是tests,用於放置特性資料夾。

calculator.py:

tests/features目錄下的特性檔案calculator.feature

從該例子不難看出特性檔案的描述是非常直截了當的,能夠使全體成員都能看明白。

特性檔案的三個要點:

  • Feature block(特性區塊):該處描述了測試組所涵蓋的程式內容。這裡不執行任何程式碼,但能使閱讀者明白正要進行什麼樣的特性測試。
  • Background block(背景區塊):先於特性檔案中每個場景(Scenario)區塊執行。這類似於SetUp()方法用於進行建立程式碼的編寫,例如進行條件和位置的編寫。
  • Scenario block(場景區塊):這裡用於定義測試。第一行用作文件再一次的描述,接著是測試的具體內容。以這樣的風格編寫測試難道不是很簡單嗎?

步驟(Steps)檔案

除了特性檔案,步驟檔案也是必須的,這是“見證奇蹟的時刻”。顯然地,特性檔案本身不會做出什麼結果;它需要步驟檔案依次地與Python執行程式碼一一對映才有最後的結果輸出。這裡應用的是正規表示式。

正規表示式?不會過於複雜嗎?其實在BDD世界裡,正規表示式常用於捕捉整個字串或從某行抓取變數。所以熟能生巧。

正規表示式?在測試中使用不會太複雜嗎?在Lettuce是不會的,反而是非常簡單的。

以下是對應的步驟檔案的編寫:

檔案首部分是標準的匯入寫法。例如對Calculator的訪問和Lettuce工具的匯入,還有nosetest包中assert_equals斷定方法的匯入。接下來,你就可以開始針對特性檔案的每一行進行步驟定義。如前所述,正規表示式很多時候用於提取整個字串,除了有時需要在某行對變數進行訪問。

在這個例子中, 裡的@step起到解碼提取的作用;u字母的意思是以unicode編碼方式進行表示式執行。接著是使用正規表示式對引用的內容進行匹配,這裡是要進行相加的數字。

然後是對Python方法傳入變數,變數名可任意定義,這裡使用x和y作為calculator add方法的傳入變數名。

此外需要介紹world變數的使用。world是一個全域性容器,使得變數可以在同一場景的不同步驟中使用。否則,所有變數只對應於其所在方法可用。例如把add方法的運算結果存放於某個step,而在另一外一個step進行結果的斷定。

特性的執行

特性檔案和步驟檔案都完成後,接下來可以執行測試來看看能否通過。內建測試執行機的Lettuce執行方式是很簡單的,例如lettuce test/features/calculator.feature:

Lettuce的輸出是非常工整的,它清楚顯示了哪行特性檔案程式碼被執行了,然後對成功執行的行以高亮綠色顯示。此外還顯示了正在執行的特性檔案以及行號,這對於測試失敗時進行特性檔案缺陷行的查詢是很有幫助的。輸出末尾是特性,場景,步驟的執行個數以及通過個數的結果彙總。本例中所有測試都通過了。但如果出現錯誤,Lettuce會如何處理呢?

首先得對calculator.py程式碼進行修改,把add方法改為兩數相減:

再次執行,看看Lettuce是如何對錯誤進行說明的:

顯然,實際結果0與預期結果4是不符的。Lettuce清楚顯示了該問題,接下來就是除錯排錯直到通過的時間了。

其它工具

在Python中還提供了很多不同的工具來進行類似的測試,這些工具基本源自Cucumber。例如:

  • Behave:這是一個Cucumber介面。文件配套齊備,保持更新,有不少的配套工具。
  • Freshen:另一個Cucumber介面,配套網站有完整的教程和例項,安裝方式都是簡單的pip方式。

不論使用什麼工具,只要對某個工具運用熟練了,其它的自然能融會貫通。對教程文件的熟讀是成功的第一步。

優點

自信地進行程式碼重構

使用一個完整測試套件的優點是顯而易見的。找到一個強大的測試套件,會讓程式碼重構工作事半功倍,信心滿滿。

隨著專案規模的不斷擴大,如果缺乏有效的工具,這不啻會使回溯和重構工作困難重重。如果有一套完整的接受測試來與每個特性一一對應,那麼將能使變更工作有序不紊地進行,不會對現有功能模組造成破壞。

全員都能參與其中的接受測試,將能極大地提升團隊戰鬥力,一開始就朝著同一目標前進。程式設計師可把精力用在精確的目標上,避免需求範圍的失控;測試員可就特性檔案進行一一檢閱,把測試環節做到極致。最後形成良性迴圈,使得程式的每個特性都完美交付。

綜述

結合上述過程和工具,在過往工作過的團隊中我們都曾取得不錯的成績。BDD開發方式可使整個團隊保持專注,保持自信,保持活力,並使潛在錯誤降到最低。

相關文章