《Cucumber:行為驅動開發指南》——6.2 同心協力
本節書摘來自異步社區《Cucumber:行為驅動開發指南》一書中的第6章,第6.2節,作者:【英】Matt Wynne , 【挪】Aslak Hellesy著,更多章節內容可以訪問雲棲社區“異步社區”公眾號查看
6.2 同心協力
6.2.1 偶然細節
考慮下麵這個為在線郵件客戶端編寫的場景:
Scenario: Check inbox
Given a User "Dave" with password "password"
And a User "Sue" with password "secret"
And an email to "Dave" from "Sue"
When I sign in as "Dave" with password "password"
Then I should see 1 email from "Sue" in my inbox
這個場景中有很多細節:有主要角色Dave的用戶名和密碼,還有另一個用戶Sue的用戶名和密碼。用戶名非常有用,因為它們有助於場景故事的描述,而密碼就是噪音了:用戶密碼與被測內容毫無關係,事實上卻讓測試更難讀懂。比如,Sue的密碼跟Dave不同。閱讀場景的時候,你會疑惑這是否重要,場景的主要目的是驗證Dave可以看Sue的郵件,你的注意力卻被分散到別處去了。
像密碼這種在場景中提及但實際上與場景的目標毫無關係的細節,我們稱之為偶然細節(incidental detail)2。這種不相關的細節使得場景很難閱讀,而這又會讓利益相關人對閱讀場景失去興趣。我們去掉密碼,把場景重寫一下:
Scenario: Check inbox
Given a User "Dave"
And a User "Sue"
And an email to "Dave" from "Sue"
When I sign in as "Dave"
Then I should see 1 email from "Sue" in my inbox
這絕對是一種改善,它使場景的實質性內容更易於閱讀和理解了。我們進一步去掉更多噪音:
Scenario: Check inbox
Given I have received an email from "Sue"
When I sign in
Then I should see 1 email from "Sue" in my inbox
現在我們有了一個簡潔清晰的三步場景。可維護性也更好:如果產品負責人(product owner)想讓我們修改身份驗證機製,我們隻需修改下層的步驟定義代碼,而不必去動特性。
避免偶然細節
如果你是一名程序員,或許早已熟練了每天在閱讀代碼的時候濾掉不相關的細節。編寫場景的時候更要時刻想著這一點,因為一不留神這些偶然細節就會熘進來。
編寫場景的時候,盡力避免被已有的步驟定義所左右,隻管用直白的英語把你希望發生的事情確切地寫下來即可。事實上,盡量不要讓程序員或測試人員獨自編寫場景。讓非技術的利益相關人或者分析師從純粹以業務為中心的角度出發編寫每個場景的初稿,或者最理想的情況是與程序員結對,從而分享他們的構思模型。有了工程意義上設計精良的支持層,你便可以信心百倍地快速編寫新的步驟定義來配合場景的表達方式。
6.2.2 命令式步驟
要讓計算機為你做事,你需要給它提供指令,在計算機程序設計中,關於如何表達這些指令有兩種對比鮮明的風格,分別稱為命令式編程(imperative programming)和聲明式編程(declarative programming)。
命令式編程是指使用一個命令序列,讓計算機按特定的次序執行它們。Ruby就是命令式語言的例子:你把程序寫成一係列的語句,Ruby按順序每次執行其中的一條語句。聲明式編程則告訴計算機應該做什麼(what),而並不精確指明如何(how)去做。CSS就是聲明式語言的例子:你告訴計算機希望Web頁麵上的各種元素如何呈現,剩下的讓計算機去處理。
Gherkin當然是命令式語言。Cucumber按照你編寫的順序依次執行場景中的每個步驟,每次執行一步。但這並不意味著這些指令讀起來要像裝配組合家具的說明書一樣。我們先來看看用命令式風格編寫場景步驟的典型例子:
Scenario: Redirect user to originally requested page after logging in
Given a User "dave" exists with password "secret"
And I am not logged in
When I navigate to the home page
Then I am redirected to the login form
When I fill in "Username" with "dave"
And I fill in "Password" with "secret"
And I press "Login"
Then I should be on the home page
這個場景有什麼好呢?好吧,它使用了非常通用的步驟的定義,如/^I fill in "(.*)" with "(.*)"$/"之類,這意味著你可以編寫大量與之類似的場景,而無須創建太多的步驟定義代碼。你或許還可以說它可以指導用戶界麵的設計,因為它為登錄表單中使用的字段和按鈕都取好了名字。
然而,團隊如果使用這樣一種命令式風格來編寫步驟定義,用不了多久他們就會遭受脆弱的測試以及厭倦的利益相關人等痛苦。以這種方式編寫的場景不僅嘈雜、冗長,讀起來令人厭煩,而且很容易遭到破壞:如果負責用戶體驗的同事決定將提交按鈕的措辭由Login改為Log in,場景就會失敗,幾乎莫名其妙地失敗。
最嚴重的是,使用這樣的泛化步驟定義寫出的場景無法創建出場景的領域語言。基於fill in和press這樣的詞語,這一場景的語言所表達的領域是用戶界麵控件,屬於泛化且層次較低的一個領域。
改用聲明式風格
我們來把場景的抽象層次提高一下,通過更加聲明式的風格來重寫場景:
Scenario: Redirect user to originally requested page after logging in
Given I am an unauthenticated User
When I attempt to view some restricted content
Then I am shown a login form
When I authenticate with valid credentials
Then I should be shown the restricted content
這種風格的漂亮之處在於它不與用戶界麵的任何特定實現相耦合。同樣的場景可應用於胖客戶端,也可應用於移動應用。它使用的不是技術詞語,而是用一種任何對網絡安全感興趣的利益相關人都能明確理解的語言(unauthenticated、restricted、credentials)編寫的。唯有從這樣的抽象層次上表達每一個場景,團隊的通用語言方得顯現。
從命令式到聲明式的風格頻譜
Gherkin特性中在命令式風格和聲明式風格之間並沒有明確的界線。相反,這是一個連續的頻譜,每個場景中每個步驟在頻譜上的正確位置取決於許多方麵:你所描述的係統領域,你所構建的應用類型,程序員的領域知識,以及非技術利益相關人對程序員的信任水平。如果利益相關人希望在特性中看到許多細節,這或許表明你需要努力改善這一信任,但也可能說明你們開發的係統就是需要詳述很多細 節的。
聲明式風格也可能被用得太過,從場景中去掉的細節太多,結果它連一個故事都講不具體了:
Scenario: The whole system
Given the system exists
When I use it
Then it should work, perfectly
這個場景當然是荒謬可笑的,但它說明了當你把抽象層次抬得太高,以至於場景不能告訴閱讀者任何能引起興趣的內容時結果會怎樣。使用這一場景的團隊需要對它們的程序員有不可思議的信任程度。我們鼓勵你督促團隊向頻譜中更抽象、更聲明式的一端努力,然而,最重要的永遠是跟你們的利益相關人一起工作,從而找出最適合他們的抽象層次。
使用命令式風格的確意味著你必須編寫更多的步驟定義,但你可以把實際工作推給支持代碼中的輔助方法(helper method),從而使步驟定義的代碼保持簡短和易於維護。我們將在第8章向你展示這種做法。
6.2.3 重複
所有好的計算機程序員都明白重複對於代碼的可維護性有多大的害處,然而我們還是經常看到團隊的Cucumber特性中充滿了重複。重複顯然會讓你的場景脆弱不堪,此外也讓場景讀起來單調乏味。
我們在第5章中演示過,Gherkin提供了可用來減少重複的Background和Scenario Outline關鍵字,但有時重複是一個信號,說明編寫步驟所用的抽象層次太低,要對這種情況保持警覺。最好跟團隊中的非技術成員一道工作,獲得他們的反饋:哪種重複他們可以接受,哪種重複會讓他們目光呆滯。
6.2.4 語言不通用
團隊使用的通用語言將由係統涉及的領域來驅動。如果你們在構建麵向現場音樂愛好者的係統,通用語言將包含音樂會、演出、表演者和場地之類的詞語。如果你們在做電視節目預告,通用語言中將有播音員、節目類型、節目長度和播出日期之類的詞語。
關鍵在於團隊的所有成員在所有場合都使用同樣的詞語。如果一個數據庫表名叫tbl_Performer,而其中的數據行所表示的東西被團隊中多數人稱為artists,那是不可接受的。每當出現這樣的術語分歧的時候,大家應該馬上停下來,確定哪個才是應該使用的,適當糾正後就堅持使用它。
我們討論的是開發一種通用語言,因為這是一個持續進行的過程。這一開發過程需要我們工作上的投入。真正做到彼此傾聽並且就使用的詞語達成一致是需要努力的,而堅持這種約定也是需要紀律的。
然而,回報是巨大的。使用通用語言的團隊犯錯更少,且更能享受工作的樂趣,因為他們能有效地溝通工作內容。不重視通用語言價值的團隊將會粗枝大葉地使用場景中的語匯,從而喪失在團隊中專注技術和專注業務的雙方之間構築堅固橋梁的寶貴機會。那時,如果你試圖糾正別人或澄清術語,帶給人的感覺隻會是吹毛求疵。
花點時間向團隊解釋一下通用語言的概念及益處。一旦大家都理解它為什麼重要,你會發現大家會更樂於花精力討論並決定使用哪些詞語更合適。
如果用得正確,Cucumber可以幫助團隊把通用語言開發出來。在程序員和業務人員協同編寫場景的時候,你會發現關於用詞精確性的各種爭論會時不時地暴發。非常好!每一次分歧都暴露了兩班人馬之間一處潛在的誤解,或者說一個bug。對新團隊來說,這樣的討論開始會困難一些,但隨著這一語言的不斷開發,事情將變得越來越容易。下麵的“三位朋友”提供了組織這種會議一種好方法。
6.2.5 閉門造車式的特性
人們會覺得Cucumber是一種技術性較強的工具。它從命令行運行,特性文件也被設計成需要與被測代碼一道簽入版本控製係統。然而它卻以幫助提高團隊的業務利益相關人對開發過程的控製感為目標。當測試和開發盡情把特性塞入版本控製的時候,團隊中其他人會覺得他們的文檔被鎖進了櫃子,而他們卻沒有鑰匙。
你的Gherkin特性可以充當描述新特性的設計工具,同時對係統已有的行為也是極好的參考文檔。對一個具備顯著規模的係統來說,沒有哪個人都準確記住它在每種情況下的行為,因此,當你收到來自用戶的bug報告,或者考慮為係統的某部分加入新功能的時候,自然希望這些參考就在觸手可及的地方。
對於分享特性從而讓非技術成員也能訪問,Cucumber本身隻提供了有限的支持,但在Cucumber周圍,大量能夠提供這種支持的插件和工具不斷出現。舉例來說,如果用GitHub做版本控製,你的項目頁麵會顯示語法高亮的特性,人們甚至可以在上麵添加評論。
Relish4是由來自Cucumber和RSpec團隊的成員創建的一項服務,旨在提供一種方便的途徑來將Cucumber特性作為文檔發布。RSpec項目目前就在用自己的Relish文檔作為主頁,你的團隊也可以這麼使用。
你隻需要堅持做到同業務利益相關人一起坐下來協作編寫場景,就足以收獲Cucumber至少一半的好處。這一過程所激發的交談會解開太多太多的潛在bug或時間延誤,即便你從不打算將特性自動化,也已經收獲頗豐了。
然而,如果你還是希望自動化,那就繼續閱讀接下來的內容,看看到底怎樣做才好。
最後更新:2017-06-05 12:31:25