March 20 08:01~10:08
![屏幕截图 2017-03-20 09.51.35 屏幕截图 2017-03-20 09.51.35](https://lh3.googleusercontent.com/-Fm1IhZrDmG0/WM85zCpNcdI/AAAAAAABf6w/OkpkbqzS97o/OU*%2525C3%2525BE%2525202017-03-20%25252009.51.35_thumb%25255B3%25255D.png?imgmax=800)
▲終於要幫開發票功能接上使用者介面了
前言
在〈BDD(18)這是一個End-To-End的Scenario嗎?〉提到scenario儘量不要描述太多使用者介面(UI)的細節,如果要透過UI做自動化驗收測試則可以把連結到UI的程式碼寫在step definition裡面。今天介紹在「開三聯式發票scenario」加上網頁介面的三種方法。
***
直接修改Scenario
▼第一個最簡單的方式就是直接修改scenario,加上透過UI操作系統的描述。例如把原本的scenario從這樣:
![屏幕截图 2017-03-20 08.29.10 屏幕截图 2017-03-20 08.29.10](https://lh3.googleusercontent.com/-qMuaCEWD02Y/WM8wxYb6jRI/AAAAAAABf5Q/DFc3NNPjVRk/OU*%2525C3%2525BE%2525202017-03-20%25252008.29.10_thumb%25255B1%25255D.png?imgmax=800)
▼加上「Given I am on the invoice Web page」步驟,改成這樣:
![屏幕截图 2017-03-20 08.30.01 屏幕截图 2017-03-20 08.30.01](https://lh3.googleusercontent.com/-I7JIuQoCxug/WM8wzKXZdgI/AAAAAAABf5Y/AYkXN0VpMTU/OU*%2525C3%2525BE%2525202017-03-20%25252008.30.01_thumb%25255B1%25255D.png?imgmax=800)
▼如此一來只要在「Given I am on the invoice Web page」的step definition中連結到操作invoice功能的網頁就可以透過UI讓這個scenario變成完整的end-to-end scenario。
![屏幕截图 2017-03-20 08.36.23 屏幕截图 2017-03-20 08.36.23](https://lh3.googleusercontent.com/-0mOWqBbLAh0/WM8w0jYzYAI/AAAAAAABf5g/PtFY4-enyBg/OU*%2525C3%2525BE%2525202017-03-20%25252008.36.23_thumb%25255B1%25255D.png?imgmax=800)
▼這種方法並沒有描述UI細節,只增加一個步驟描述這個scenario的入口點,其實是可以接受的方法。如果一個feature檔案中有多個scenario,也可以利用Cucumber的Background功能,統一把「Given I am on the invoice Web page」放在Background中,這樣就不用每一個scenario都重複描述。
![屏幕截图 2017-03-20 08.39.57 屏幕截图 2017-03-20 08.39.57](https://lh3.googleusercontent.com/-qLxsn18frNk/WM8w3RPn8nI/AAAAAAABf5o/NxjqV_kizJQ/OU*%2525C3%2525BE%2525202017-03-20%25252008.39.57_thumb%25255B1%25255D.png?imgmax=800)
如果覺得日後開發票功能可能會改成手機或桌機版本,到時候要把「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。
![屏幕截图 2017-03-20 08.29.10 屏幕截图 2017-03-20 08.29.10](https://lh3.googleusercontent.com/-N6lKV-85eII/WM8w5FFRWTI/AAAAAAABf5w/N2Cx3E8rbo4/OU*%2525C3%2525BE%2525202017-03-20%25252008.29.10_thumb%25255B2%25255D.png?imgmax=800)
▼第二種做法就是直接把連結到UI的程式碼寫在step definition裡面。這種做法的好處是scenario專心描述企業邏輯,當真的需要連結到UI的時候只需要修改step definition即可,不用動到scenario。
![屏幕截图 2017-03-20 08.51.01 屏幕截图 2017-03-20 08.51.01](https://lh3.googleusercontent.com/-gfX8KV0m-Cg/WM8w64uQFwI/AAAAAAABf54/B3VxxhRSsIU/OU*%2525C3%2525BE%2525202017-03-20%25252008.51.01_thumb%25255B1%25255D.png?imgmax=800)
這種做法有兩個小缺點,首先從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。
![屏幕截图 2017-03-20 08.09.40 屏幕截图 2017-03-20 08.09.40](https://lh3.googleusercontent.com/-rypGnYXu9Wo/WM8w9FGdV_I/AAAAAAABf6A/oDntK3tNOgQ/OU*%2525C3%2525BE%2525202017-03-20%25252008.09.40_thumb%25255B1%25255D.png?imgmax=800)
▼為了讓step definition更乾淨,可以在step definition與domain object之間增加一個support layer(支援層),透過這一層來操作domain object。如此一來便可把UI的細節封裝在support layer。
![屏幕截图 2017-03-20 09.04.43 屏幕截图 2017-03-20 09.04.43](https://lh3.googleusercontent.com/-zZMaXiKiGW0/WM8w_XbZN5I/AAAAAAABf6I/X5gbCiEfxLo/OU*%2525C3%2525BE%2525202017-03-20%25252009.04.43_thumb%25255B3%25255D.png?imgmax=800)
▼原本step definition直接操作domain object,現在改成透過KnowsTheDomain類別來操作domain object。
![屏幕截图 2017-03-20 09.18.59 屏幕截图 2017-03-20 09.18.59](https://lh3.googleusercontent.com/-q97PEL59jLQ/WM8xBcYKNKI/AAAAAAABf6Q/F7e9R6LgJcY/OU*%2525C3%2525BE%2525202017-03-20%25252009.18.59_thumb%25255B2%25255D.png?imgmax=800)
▼KnowsTheDomain類別很簡單,只是把原本step definition直接操作domain object的程式碼封裝在這裡而已。
![屏幕截图 2017-03-20 09.17.02 屏幕截图 2017-03-20 09.17.02](https://lh3.googleusercontent.com/-Ya4zB6nsExs/WM8xDJ8ZTZI/AAAAAAABf6Y/e0m2C_qK1SM/OU*%2525C3%2525BE%2525202017-03-20%25252009.17.02_thumb%25255B2%25255D.png?imgmax=800)
現在思考一下如何重構(refactor)讓我們可以輕鬆切換有UI和沒有UI的自動化驗收測試。在原本的domain object中,開發票的責任由InvoiceBuilder類別負責,只要能夠提供兩種InvoiceBuilder的實作,一種是原本的實作,另外一種是包含UI的實作,如此一來只要讓KnowTheDomain類別的getInvoiceBuilder函數可以依據設定傳回不同的InvoiceBuilder類別實作,就可以達到「輕鬆切換有UI和沒有UI的自動化驗收測試」的目的。這種做法不需要修改scenario,也不用動到step definition。
▼首先套用Extract Interface重構,把原本InvoiceBuilder類別的介面抽離出來。
![屏幕截图 2017-03-20 09.38.18 屏幕截图 2017-03-20 09.38.18](https://lh3.googleusercontent.com/-eO5IYE0up9I/WM850gSIrfI/AAAAAAABf64/NgMLUkUHsIY/OU*%2525C3%2525BE%2525202017-03-20%25252009.38.18_thumb%25255B1%25255D.png?imgmax=800)
▼原本的InvoiceBuilder類別改名為InvoiceBuilderImpl,並且實作剛剛抽離出來的InvoiceBuilder介面。
![屏幕截图 2017-03-20 09.40.31 屏幕截图 2017-03-20 09.40.31](https://lh3.googleusercontent.com/-iTppA4IIwG4/WM852syDOyI/AAAAAAABf7A/xNp5bHQMB_M/OU*%2525C3%2525BE%2525202017-03-20%25252009.40.31_thumb%25255B1%25255D.png?imgmax=800)
▼新增WebInvoiceBuilder類別,讓它繼承InvoiceBuilderImpl。複寫issue函數,在開發票的時候打開瀏覽器連到開發票系統的網頁畫面,透過網頁完成開發票的動作。
![屏幕截图 2017-03-20 09.42.37 屏幕截图 2017-03-20 09.42.37](https://lh3.googleusercontent.com/-6PAO4iu_qdE/WM854b3sbRI/AAAAAAABf7I/E-tlpu55ED0/OU*%2525C3%2525BE%2525202017-03-20%25252009.42.37_thumb%25255B1%25255D.png?imgmax=800)
▼最後透過dependency injection容器(在此使用picocontainer),讀取設定檔來判斷要注入WebInvoiceBuilder類別實例還是InvoiceBuilderImpl類別實例。
![屏幕截图 2017-03-20 09.46.12 屏幕截图 2017-03-20 09.46.12](https://lh3.googleusercontent.com/-b9WKiK6OV3Q/WM856G6RS1I/AAAAAAABf7Q/jcVS4TsmPIU/OU*%2525C3%2525BE%2525202017-03-20%25252009.46.12_thumb%25255B1%25255D.png?imgmax=800)
▼如果要透過網頁測試,在設定檔中加入這一行。
![屏幕截图 2017-03-20 09.49.35 屏幕截图 2017-03-20 09.49.35](https://lh3.googleusercontent.com/-zLjBRX2isSk/WM85710USLI/AAAAAAABf7Y/ZcAqMQoequs/OU*%2525C3%2525BE%2525202017-03-20%25252009.49.35_thumb%25255B1%25255D.png?imgmax=800)
▼執行驗收測試,首先連結到開發票網頁,自動輸入含稅金額與稅率,按下開發票按鈕。
![屏幕截图 2017-03-20 09.51.58 屏幕截图 2017-03-20 09.51.58](https://lh3.googleusercontent.com/-xi17dTuuNRA/WM859iSW1AI/AAAAAAABf7g/FvdEbs2qUcA/OU*%2525C3%2525BE%2525202017-03-20%25252009.51.58_thumb%25255B1%25255D.png?imgmax=800)
▼接著看到計算後的稅額和未稅金額。
![屏幕截图 2017-03-20 09.51.35 屏幕截图 2017-03-20 09.51.35](https://lh3.googleusercontent.com/-VMkARwYOWpA/WM85_aZP5nI/AAAAAAABf7o/P70W8ixgfZY/OU*%2525C3%2525BE%2525202017-03-20%25252009.51.35_thumb%25255B1%25255D.png?imgmax=800)
▼當然驗收測試也是通過的。
![屏幕截图 2017-03-20 09.52.26 屏幕截图 2017-03-20 09.52.26](https://lh3.googleusercontent.com/-WCL5Gqelp9k/WM86A8HSQ5I/AAAAAAABf7w/vh7XP1matO8/OU*%2525C3%2525BE%2525202017-03-20%25252009.52.26_thumb%25255B2%25255D.png?imgmax=800)
***
結論
今天介紹三種scenario串接UI的方法,各有其優缺點。第三種方法看起來最漂亮,但需要花多點設計與重構的功夫。如果鄉民們想要使用第三種方法但覺得scenario裡面沒有敘述功能起始點怪怪的,也可以結合第一種和第三種方法,把功能起始點描述在feature檔的Background裡面,scenario連結UI的實作方式則是用第三種方法來完成,這樣子應該就接近完美了XD。
***
友藏內心獨白:什麼接近完美,明明就很麻煩…Orz。