2015-06-07

[心得文] 自動測試與TDD實務開發 Day2

Day2 的教學順序是先教我們怎麼使用SeleniumWeb-Testing;中間則是介紹了FluentAutomation以及Page Objects;最後則是帶到了Refactoring

而我的觀戰重點則是在於Refactoring。還記得以前我的Refactoring都是耗時費力,由上往下的設計模式。Refactor完也不敢保證有沒有side effectDay2則是學到了如何有效攻克Refactoring。確實相當實用阿~

Joey提供的Lab很寫實地反映了一個可以執行卻是寫得很爛的設計。你不能說他錯,前輩們的智慧讓這段code是可以執行的。但是它的確設計的很爛,邏輯全都包在一個btnXXX_Click Method中,很眼熟吧?是不是很多人都這樣設計呢?

還是要說,Joey這份Lab : lab6-RefactoringWebStie,著實設計的不錯,讓我們一步一步地Refactoring。這份Lab的題目是根據貨品的重量、長、寬、高以及不同的物流商,我們提供不同的運費。接下來就看看一步步該注意的原則吧。

1. 首先,必須要建立測試。

通常我們若沒有寫Unit Test的習慣,Refactoring就像之前我的耗力費時的作品一樣,是沒有效率的。我常常聽到別人不寫測試或是不敢Refactoring的理由是:
    (1) ~ 以前就沒寫UT了阿~ 現在想要也來不及加了~
    (2) ~ UT很好,但適合New Project,我們不適合。
    (3) ~ 我們都是Legacy code,沒辦法測的啦~
    (4) 這影響範圍太大,我怕有Side Effect。先不要動好了。

很高興在這堂課我看見了曙光。沒有Unit Test沒關係,我就建立一個外圍最大包的測試。這樣總行了吧?藉由一個外圍最大包的整合測試來保護,我們就能夠安心地Refactoring了,一旦Refactor失敗,測試就告訴你Fail。經由這快速的回饋,我們就能夠一步步把Refactoring做好。
於是乎,Joey就帶到了Web-Testing。如果我們的ProjectWeb類型的,就可以利用Selenium這套Tool來錄下WebUITesting Script並驗證一些行為。而Selenium更強大的是,它能夠將Testing Script轉變成NUnitTesting Code,並由Test Project去驅動這個測試。這樣就能在Test Case中自動化地驗證Web的行為。

如此,你最外頭已經有測試保護了,接下來就能放心地做Refactoring

補充:或許QA會覺得Selenium轉化成的Testing Code不夠直覺,所以可以參考能提高Testing Code可讀性與維護性的FluentAutomationPage Objects Pattern (Selenium官方推薦的方式)

2. 讓程式邏輯與網頁UI分離。
很多人習慣讓WebForm控制項的邏輯寫在cs中。
For example

這些code都跟UI耦合,從控制項中取得值,並運算一些邏輯之後再填回去控制項。

其實我們可以考慮從這裡將WebForm控制項抽離,用一個自行定義Object (方便抽換),來取代,這樣可以降低耦合
For example :


3. 重構-擷取方法 : 先將一大串code 擷取成Method

下一步則是將混在一起的邏輯運算擷取出來變成一個Method

4. 重構-職責分離(主動詞分離),將商業邏輯從網站抽到 library


原本計算運費的商業邏輯跟網頁混在一起,很難去各別測計算運費的,所以我們可以將商業邏輯抽到Library去,這樣就能測它了。這邊要注意的是,主動詞分離的原則。
For exampleUser選擇黑貓這個物流商,那麼根據商品屬性以及黑貓的運算邏輯,我們可以設計出Blackcat這個Class以及它有一個計算運費的MethodCalculateFee。而這個Method的內容就可以塞入Step3剛剛擷取的Method

到了這一步之後,我們就能確保網頁應該不包含任何計算運費的邏輯,因為我們已經將計算運費的職責委託給Blackcat這個Class來處理

5. Unit Test - Library建立Unit Test

Step4將邏輯切到Library之後,我們就能對它做Unit Test。看吧!到了這一步,是不是就能做測試了呢?

6. 持續Refactoring
有了最外圍的整合測試加上Library的單元測試,我們就能放心地持續一直Refactor下去,無論你是想要擷取成介面或是考慮將生成物件的職責獨立出來都能放心地做了。只要測試有通過就好。

以上幾點原則,就是攻克一大坨code的方法。
補充一個我很欣賞Joey的一個手法:Focus on the current content

TipsFocus on the current content
由小處著手,從這段code出發,不會跳來跳去。這實作其實蠻難描述的,但就是藉著IDE的協助,當需要某個PropertyMethod或是Class時先用手刻一個Name,再透過IDE自動產生對應的code。讓programmer不須跳來跳去地自己產生。如此專注的結果也反映了寫code的效率

Extract and Override
解legacy code的暫時解,非正統解
將切不掉的東西,放到private method,並更改為protected virtual
Test Code再繼承這個Class之後覆寫掉
我們可以先用這個解法先建立起測試,之後再用正統的方法Refactoring這段。

最後想說的是
原則是這樣沒錯,但還是需要練習練習再練習
練習是不會騙人的。
也不要再說範圍太大不可以測了,如何一步步把範圍縮小到可以測就是我們接下來的事了。

補充: Selenium其實是一種化學元素 - 硒
補充: 可用CodeMaid來分析Method的複雜度

系列文章
[心得文] 自動測試與TDD實務開發 Day1

0 意見: