天天看點

《Cucumber:行為驅動開發指南》——6.2 同心協力

本節書摘來自異步社群《cucumber:行為驅動開發指南》一書中的第6章,第6.2節,作者:【英】matt wynne , 【挪】aslak hellesy著,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

6.2.1 偶然細節

考慮下面這個為線上郵件用戶端編寫的場景:

這個場景中有很多細節:有主要角色dave的使用者名和密碼,還有另一個使用者sue的使用者名和密碼。使用者名非常有用,因為它們有助于場景故事的描述,而密碼就是噪音了:使用者密碼與被測内容毫無關系,事實上卻讓測試更難讀懂。比如,sue的密碼跟dave不同。閱讀場景的時候,你會疑惑這是否重要,場景的主要目的是驗證dave可以看sue的郵件,你的注意力卻被分散到别處去了。

像密碼這種在場景中提及但實際上與場景的目标毫無關系的細節,我們稱之為偶然細節(incidental detail)2。這種不相關的細節使得場景很難閱讀,而這又會讓利益相關人對閱讀場景失去興趣。我們去掉密碼,把場景重寫一下:

這絕對是一種改善,它使場景的實質性内容更易于閱讀和了解了。我們進一步去掉更多噪音:

現在我們有了一個簡潔清晰的三步場景。可維護性也更好:如果産品負責人(product owner)想讓我們修改身份驗證機制,我們隻需修改下層的步驟定義代碼,而不必去動特性。

避免偶然細節

如果你是一名程式員,或許早已熟練了每天在閱讀代碼的時候濾掉不相關的細節。編寫場景的時候更要時刻想着這一點,因為一不留神這些偶然細節就會溜進來。

編寫場景的時候,盡力避免被已有的步驟定義所左右,隻管用直白的英語把你希望發生的事情确切地寫下來即可。事實上,盡量不要讓程式員或測試人員獨自編寫場景。讓非技術的利益相關人或者分析師從純粹以業務為中心的角度出發編寫每個場景的初稿,或者最理想的情況是與程式員結對,進而分享他們的構思模型。有了工程意義上設計精良的支援層,你便可以信心百倍地快速編寫新的步驟定義來配合場景的表達方式。

6.2.2 指令式步驟

要讓計算機為你做事,你需要給它提供指令,在計算機程式設計中,關于如何表達這些指令有兩種對比鮮明的風格,分别稱為指令式程式設計(imperative programming)和聲明式程式設計(declarative programming)。

指令式程式設計是指使用一個指令序列,讓計算機按特定的次序執行它們。ruby就是指令式語言的例子:你把程式寫成一系列的語句,ruby按順序每次執行其中的一條語句。聲明式程式設計則告訴計算機應該做什麼(what),而并不精确指明如何(how)去做。css就是聲明式語言的例子:你告訴計算機希望web頁面上的各種元素如何呈現,剩下的讓計算機去處理。

gherkin當然是指令式語言。cucumber按照你編寫的順序依次執行場景中的每個步驟,每次執行一步。但這并不意味着這些指令讀起來要像裝配組合家具的說明書一樣。我們先來看看用指令式風格編寫場景步驟的典型例子:

這個場景有什麼好呢?好吧,它使用了非常通用的步驟的定義,如/^i fill in "(.)" with "(.)"$/"之類,這意味着你可以編寫大量與之類似的場景,而無須建立太多的步驟定義代碼。你或許還可以說它可以指導使用者界面的設計,因為它為登入表單中使用的字段和按鈕都取好了名字。

然而,團隊如果使用這樣一種指令式風格來編寫步驟定義,用不了多久他們就會遭受脆弱的測試以及厭倦的利益相關人等痛苦。以這種方式編寫的場景不僅嘈雜、冗長,讀起來令人厭煩,而且很容易遭到破壞:如果負責使用者體驗的同僚決定将送出按鈕的措辭由login改為log in,場景就會失敗,幾乎莫名其妙地失敗。

最嚴重的是,使用這樣的泛化步驟定義寫出的場景無法建立出場景的領域語言。基于fill in和press這樣的詞語,這一場景的語言所表達的領域是使用者界面控件,屬于泛化且層次較低的一個領域。

改用聲明式風格

我們來把場景的抽象層次提高一下,通過更加聲明式的風格來重寫場景:

這種風格的漂亮之處在于它不與使用者界面的任何特定實作相耦合。同樣的場景可應用于胖用戶端,也可應用于移動應用。它使用的不是技術詞語,而是用一種任何對網絡安全感興趣的利益相關人都能明确了解的語言(unauthenticated、restricted、credentials)編寫的。唯有從這樣的抽象層次上表達每一個場景,團隊的通用語言方得顯現。

從指令式到聲明式的風格頻譜

gherkin特性中在指令式風格和聲明式風格之間并沒有明确的界線。相反,這是一個連續的頻譜,每個場景中每個步驟在頻譜上的正确位置取決于許多方面:你所描述的系統領域,你所建構的應用類型,程式員的領域知識,以及非技術利益相關人對程式員的信任水準。如果利益相關人希望在特性中看到許多細節,這或許表明你需要努力改善這一信任,但也可能說明你們開發的系統就是需要詳述很多細 節的。

聲明式風格也可能被用得太過,從場景中去掉的細節太多,結果它連一個故事都講不具體了:

這個場景當然是荒謬可笑的,但它說明了當你把抽象層次擡得太高,以至于場景不能告訴閱讀者任何能引起興趣的内容時結果會怎樣。使用這一場景的團隊需要對它們的程式員有不可思議的信任程度。我們鼓勵你督促團隊向頻譜中更抽象、更聲明式的一端努力,然而,最重要的永遠是跟你們的利益相關人一起工作,進而找出最适合他們的抽象層次。

使用指令式風格的确意味着你必須編寫更多的步驟定義,但你可以把實際工作推給支援代碼中的輔助方法(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或時間延誤,即便你從不打算将特性自動化,也已經收獲頗豐了。

然而,如果你還是希望自動化,那就繼續閱讀接下來的内容,看看到底怎樣做才好。