March 20 08:01~10:08
▲終於要幫開發票功能接上使用者介面了
前言
在〈BDD(18)這是一個End-To-End的Scenario嗎?〉提到scenario儘量不要描述太多使用者介面(UI)的細節,如果要透過UI做自動化驗收測試則可以把連結到UI的程式碼寫在step definition裡面。今天介紹在「開三聯式發票scenario」加上網頁介面的三種方法。
***
直接修改Scenario
▼第一個最簡單的方式就是直接修改scenario,加上透過UI操作系統的描述。例如把原本的scenario從這樣:
▼加上「Given I am on the invoice Web page」步驟,改成這樣:
▼如此一來只要在「Given I am on the invoice Web page」的step definition中連結到操作invoice功能的網頁就可以透過UI讓這個scenario變成完整的end-to-end scenario。
▼這種方法並沒有描述UI細節,只增加一個步驟描述這個scenario的入口點,其實是可以接受的方法。如果一個feature檔案中有多個scenario,也可以利用Cucumber的Background功能,統一把「Given I am on the invoice Web page」放在Background中,這樣就不用每一個scenario都重複描述。
如果覺得日後開發票功能可能會改成手機或桌機版本,到時候要把「Given I am on the invoice Web page」改成「Given I am on the invoice App screen」這樣很麻煩,也可以把這句改成更中性一點的敘述:「Given I am using the invoice function」,這樣子scenario要接Web、App、或桌上型應用軟體只要改step definition的內容就可以,不需修改feature檔案(不用改需求)。
***
寫在Step Definition裡面
▼如果想維持原本的scenario敘述不變,但又想要透過UI來自動化這個scenario。
▼第二種做法就是直接把連結到UI的程式碼寫在step definition裡面。這種做法的好處是scenario專心描述企業邏輯,當真的需要連結到UI的時候只需要修改step definition即可,不用動到scenario。
這種做法有兩個小缺點,首先從scenario檔案看不出每一個scenario的明顯「入口點」。第二個缺點是step definition內容變得有點亂,原本step definition是協助開發人員探索與定義系統實作的介面,現在夾雜了UI的自動化測試程式,很容易混淆原本的意圖。
***
增加Support Layer
▼原本使用Cucumber的步驟由定義feature file開始,接著撰寫step definition,然後透過TDD的方式來完成domain object(production code)的設計與開發。因為step definition直接操作domain object,所以如果要加上透過UI來操作domain object就必須要修改step definition。
▼為了讓step definition更乾淨,可以在step definition與domain object之間增加一個support layer(支援層),透過這一層來操作domain object。如此一來便可把UI的細節封裝在support layer。
▼原本step definition直接操作domain object,現在改成透過KnowsTheDomain類別來操作domain object。
▼KnowsTheDomain類別很簡單,只是把原本step definition直接操作domain object的程式碼封裝在這裡而已。
現在思考一下如何重構(refactor)讓我們可以輕鬆切換有UI和沒有UI的自動化驗收測試。在原本的domain object中,開發票的責任由InvoiceBuilder類別負責,只要能夠提供兩種InvoiceBuilder的實作,一種是原本的實作,另外一種是包含UI的實作,如此一來只要讓KnowTheDomain類別的getInvoiceBuilder函數可以依據設定傳回不同的InvoiceBuilder類別實作,就可以達到「輕鬆切換有UI和沒有UI的自動化驗收測試」的目的。這種做法不需要修改scenario,也不用動到step definition。
▼首先套用Extract Interface重構,把原本InvoiceBuilder類別的介面抽離出來。
▼原本的InvoiceBuilder類別改名為InvoiceBuilderImpl,並且實作剛剛抽離出來的InvoiceBuilder介面。
▼新增WebInvoiceBuilder類別,讓它繼承InvoiceBuilderImpl。複寫issue函數,在開發票的時候打開瀏覽器連到開發票系統的網頁畫面,透過網頁完成開發票的動作。
▼最後透過dependency injection容器(在此使用picocontainer),讀取設定檔來判斷要注入WebInvoiceBuilder類別實例還是InvoiceBuilderImpl類別實例。
▼如果要透過網頁測試,在設定檔中加入這一行。
▼執行驗收測試,首先連結到開發票網頁,自動輸入含稅金額與稅率,按下開發票按鈕。
▼接著看到計算後的稅額和未稅金額。
▼當然驗收測試也是通過的。
***
結論
今天介紹三種scenario串接UI的方法,各有其優缺點。第三種方法看起來最漂亮,但需要花多點設計與重構的功夫。如果鄉民們想要使用第三種方法但覺得scenario裡面沒有敘述功能起始點怪怪的,也可以結合第一種和第三種方法,把功能起始點描述在feature檔的Background裡面,scenario連結UI的實作方式則是用第三種方法來完成,這樣子應該就接近完美了XD。
***
友藏內心獨白:什麼接近完美,明明就很麻煩…Orz。