天天看點

《驗收測試驅動開發:ATDD執行個體詳解》—第2章2.1節第一個測試用例

本節書摘來自異步社群《驗收測試驅動開發:atdd執行個體詳解》一書中的第2章2.1節第一個測試用例,作者【德】markus gärtner,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

第2章 代客泊車的測試自動化

驗收測試驅動開發:atdd執行個體詳解

團隊決定從表1-11所示的停車場故事的代客泊車的執行個體開始做。大家決定使用cucumber1來實作測試自動化。cucumber使用ruby語言将執行個體的資料表示和被測試系統粘合在一起。在cucumber中,每個測試集合被稱為一個特性(feature),每個特性由一個單獨的文本檔案來描述。

為了使用cucumber來實作測試自動化,我們需要一組特性來記錄測試資料,一些用來描述與被測應用互動的測試步驟定義,以及一套環境設定資訊。

tony腦海中的總體架構如圖2-1所示。

《驗收測試驅動開發:ATDD執行個體詳解》—第2章2.1節第一個測試用例

最頂端的那些執行個體是在讨論會中确定的。現在,tony開始将它們導入cucumber。cucumber需要一些粘合代碼以執行被測應用。粘合代碼可以分為步驟定義、支援代碼和第三方庫(例如selenium中驅動浏覽器的代碼庫)。

tony計劃将停車費電腦中的支援代碼放入一個單獨的庫中,以便步驟定義中的粘合代碼可以使用這個庫。

自動化測試環境的建立需要使用selenium2。自動測試代碼通過selenium可以驅動浏覽器,使得自動測試代碼可以與網頁互動并且驗證取值的正确性,例如停車費用的計算結果。團隊建立了一個持續內建系統,并連接配接了一個headless selenium伺服器3。這樣測試代碼就可以在建構中連接配接伺服器了。

headless selenium伺服器在一個虛拟的伺服器上運作浏覽器。使用headless selenium伺服器,你可以在一個沒有顯示器的機器上運作所有的測試。

本文僅用于學習和交流目的,不代表異步社群觀點。非商業轉載請注明作譯者、出處,并保留本文的原始連結。

2.1 第一個測試用例

tony首先選了“30分鐘”這個用例。他開始在valet.feature檔案中描述這個代客泊車的特性。tony在開讨論會用的桌邊完成了他的第一個測試(見程式清單2-1)。

程式清單2-1 tony的第一個代客泊車測試

現在,代客泊車特性有了第一個“30分鐘”的測試。期望的停車費是12美元,就像在讨論會中确定的一樣。在程式清單2-1所示的代碼中,第1行描述了我們要測試的特性的名字。第2行是進一步的說明。cucumber運作時會在控制台顯示這個說明。tony通常在這裡交代他的測試意圖,以便後來的測試編寫人員了解,當然,那個人很可能就是幾個月後的他自己。

在第4行中,他把第一個測試命名為“計算半小時的代客泊車費用”。由于這個測試目标就是停車30分鐘,這個名字取得很确切。第5~6行用到了關鍵字when和then,以便描述測試的兩個不同階段。

關鍵字when描述了觸發被測系統運轉應該采取的動作。可能包括調用一個函數,或者按下某個按鍵。

車位類型以及停車時間是when關鍵字的參數。cucumber将會解析這些參數,并将它們提供給系統,這樣應用程式就可以計算停車費用了。

then關鍵字描述了系統執行應用程式之後測試的後置條件,任何期望的結果都應該寫在這裡。在這個例子裡,tony加入了停車費計算結果的檢測。

現在,tony儲存valet.feature檔案,然後通過指令cucumber valet.feature讓cucumber運作它。運作結果如程式清單2-2所示。結果首先輸出了tony剛才編輯的測試場景代碼。在第8行中,運作結果提醒tony cucumber試圖運作一個場景,但是這個場景未定義。同樣在第9行中,cucumber指出有兩個未定義的步驟。從第12行開始是一個提示,提醒tony如何實作這些未定義的步驟。

程式清單2-2 第一個代客泊車測試的指令行輸出

遵照cucumber的提示,tony建立了一個名為valet_steps.rb的檔案,編寫步驟定義,并将提示裡的示例代碼複制、粘貼到這個檔案中。為了将測試資料和在被測系統上執行指令的粘合代碼分開,tony将支援代碼(步驟定義)放在一個建立的目錄step_definitions下的一個檔案中。

tony在提示的代碼樁(stub)上做了一些修改。這樣在以後把第一個測試執行個體擴充到不同停車時長的時候會比較友善。修改結果在程式清單2-3中給出。

程式清單2-3 第一個測試最初的步驟定義

cucumber可以從測試的描述語句中分析出變量,解析停車時長和費用就用到了這個特性。pending也是cucumber可識别的關鍵字,運作時會輸出資訊,說明這個測試目前是挂起的,很可能因為這個測試正在編寫中。現在步驟定義已經就位,當tony重新執行這個測試時,得到了輸出如程式清單2-4所示。

程式清單2-4 加入了步驟定義的第一個代客泊車測試的輸出

為了能讓被測系統運作,tony還需要配置一下web浏覽器的驅動程式selenium。selenium有一個伺服器元件,還有一個用戶端庫,供支援代碼驅動浏覽器及浏覽網頁。tony在單獨的env.rb檔案中編寫了支援代碼,這樣支援代碼就可以和操作被測系統的粘合代碼分離開了。他把這個檔案放入了一個建立的etc檔案夾(檔案目錄結構見圖2-2)。他需要的每樣東西都列在程式清單2-5中。

《驗收測試驅動開發:ATDD執行個體詳解》—第2章2.1節第一個測試用例

程式清單2-5 設定selenium用戶端的支援代碼

這段支援代碼使用了selenium提供的庫,啟動一個浏覽器,然後打開parkcalc頁面。當所有的測試運作完後,它還會關閉浏覽器視窗。

檔案env.rb同樣需要一個庫檔案lib/parkcalc來進行停車費的計算。在開發測試的同時,tony将會逐漸地完善這個庫檔案。檔案的初始内容如程式清單2-6所示。

程式清單2-6 初始的parkcalcpage類

在初始化函數中,傳入的page_handle存入本地屬性page中,并打開了/parkcalc頁面。這段初始化代碼,完成了用傳入的浏覽器打開web表單的功能。傳入的浏覽器正是在env.rb中配置的那個。

tony開始分步開發他的測試。他需要完成的第一步是向web接口填入停車時長參數。tony現在還不知道具體的實作細節,但是他已經看過手繪的頁面布局的最終設計。when i park my car in the valet parking lot for< duration >語句包含兩個基本步驟。首先,選擇正确的停車場。其次需要填入代表正确停車時長的值。tony打算根據自己的主觀期望來處理這個特定的問題,他在valet_steps.rb中寫下了運作這個執行個體所需用到的api(見程式清單2-7)。

程式清單2-7 對第一個語句的預想實作

第一步對應程式清單2-7的第2行。tony認為應該有個停車場的選擇機制。根據實作細節,這可能意味着在文本框中輸入字元串,或者從選擇組合框中選中正确的停車場,又或者從一個下拉菜單中選擇停車場。由于目前tony對此還一無所知,他将這個具體實作的細節推遲到parkcalcpage類被實作。

第二步就是程式清單2-7的第3行,它描述了停車時長通過某種方式填入了頁面。同樣,這也許意味着在輸入框中輸入一個時長字元串,也許有兩個輸入框分别對應停車起始時間和結束時間,然後根據輸入計算出時長。由于具體的使用者界面實作方式還沒有敲定,tony會等到parkcalcpage類實作後再做決定。

為了表明這些測試步驟定義還未完成,tony在語句定義的最後加上了關鍵字pending。這樣一旦另外兩個關鍵字實作了就能提醒未來的實作者修改步驟定義。

現在tony為他期待實作的兩個方法提供空的實作,通過這種方式來向parkcalcpage類未來的實作者告知他對這個類接口的設計(見程式清單2-8)。

程式清單2-8 為parkcalcpage類加上空的實作

tony給parkcalcpage添加了select(parking_lot)和enter_parking_duration(duration)方法。前一個以後用來選擇停車場,後一個負責填入使用者界面提供的停車時長。

tony現在集中注意力處理他測試中的驗證步驟。同準備步驟一樣,tony同樣根據自己的主觀期望來表述停車費的最終驗證方法。程式清單2-9中展示了他對step_definitaions/valet_steps.rb檔案的修改。對parkcalcpage類必要的修改見程式清單2-10。

程式清單2-9 基于預想的第一個語句的初始實作

程式清單2-10 停車費計算步驟的空實作

tony完成了他目前能夠驅動的第一個測試。現在他面臨着如何繼續的抉擇。一方面,他可以繼續完成代客泊車需求讨論會中确定的其他執行個體。另一方面,他也可以與開發人員結對去對執行個體進行自動化,以便驅動這個特性之後的開發。第三種選擇是繼續應付剩下的4種停車場,并且在cucumber中寫好它們各自的第一個測試。tony決定和開發人員結對去實作第一個代客泊車功能并且對他的第一個測試進行自動化。這樣,tony可以獲得他對目前已完成工作的及時回報,稍後就能繼續開發其他測試了。這麼做還有一個好處,即tony不會在實作開始之前加入太多無法通過的測試。

繼續閱讀