2013年7月13日 星期六

[Engineering Practice] 軟體開發的測試-單元測試 / Software Development Testing - Unit Testing

以前只要講到系統功能要測試,通常就會想到找 PM、SA或直接給客戶進行人體測試,結果就會發生一使用功能就 Error 的悲劇... 。今年去上 Agile Project Management 時, 講到強調軟體開發的Extreme Programming時,特別強調測試,在 "XP 規則" ( The Rules of Extreme Programming原文 )中,有一組規則是講測試的,貼上來給大家震撼一下:
  • All code must have unit tests. 所有程式碼都要有單元測試 !!!!!
  • All code must pass all unit tests before it  can be released. 所有程式碼通過單元測試後才能release !
  • When a bug is found tests are created. 如果有發現 bug 時,就要回去寫測試 !!!
  • Acceptance tests are run often and the score is published.  經常執行驗收測試,並且必須公布量化指標!!
對XP Unit Test 有興趣的朋友,可以再看這一篇: Unit Test ..

既然單元測試是這麼重要,在上完Agile的課後,小弟開始學習在.Net Framework 要怎麼做單元測試,這一篇算是介紹,也是對自己學習的筆記,我使用的工具是 C# 與 Visual Studio 2012,使用 Java 的朋友,你一定能找到對應的工具與平台,這年頭, M$ 會推出某個功能,都是被 Java Community 逼出來的..ㄎㄎ... 廢話不多說,我用問題與回答來說明單元測試:

1. 單元測試到底是指甚麼?  =>預期結果等於測試結果
  • 簡單說,單元測試是對程式的最基本的單位進行測試 ( 最基本單位是要看你自己認定,小弟是非常程序導向的開發人員 i.e. old ,我的通常是 Function ),確認程式能夠如預期的執行。小弟的心得是,因為要單元測試,會強迫自己開始規劃自己的程式,讓 function 真正能比較像一個 function 該做的事,不會通通塞到一起才來拼命 debug line by line (還記得以前老師教的高聚合低耦合嗎?... )
  • 在單元測試會做的三件事:這跟人體測試時的邏輯是滿相同的,假設你要呼叫一個兩數相除的 function,會有3 步驟
    • Step (1) 先準備呼叫的資料,如 100除以5,所以你會把100跟5先準備好,這個步驟就是 "安排測試資料(Arrange)"
    • Step (2) 開始呼叫Function,並取得結果,譬如這個function可正常呼叫並回傳20,這個步驟就是 "執行測試(Act)"
    • Step (3) "判斷結果(Assert)其實在提供資料去測試時,你會有預期的結果,所以從第2步取得結果後,就會跟你預期的結果進行比對,以判斷兩數相除的Function是否如果預期執行,譬如 100/5=20,function回傳結果也是20,所以你就可以判斷function是否運作正常了!
    • 下圖是我寫測試一個上傳檔案的 Function 是否正確的單元測試程式,其實跟真正要呼叫該 Function 的程式會很類似,所以其實單元測試寫完,就可以copy/paste到正是用的程式,一魚兩吃阿!


2. 要如何開始做單元測試 ? =>打開你的好朋友(?!) Visual Studio
  • 這個問題跟使用的程式語言與開發工具非常相關了,我用Visual Studio 2012來當範例,基本操作在網路上已經很多教學怎麼做了 (如果跟我一樣從未接觸過,可以參考這一篇 MSDN,照著做一次會有感覺...) ,步驟大致是:
    • (1)在你目前開發的"方案"中,加入一個 "單元測試專案"
    • (2)在新的單元測試專案中,增加"參考" 開發的專案
    • (3)在新的單元測試專案中,開始寫Arrange, Act, Assert的程式。 請注意的是,在一個單元測試中,可以放多筆測試資料,預設是只要一個測試未通過,就會當作不成功,可以多放幾筆確定 function 真的跟預期的結果相同 (該有回傳就回傳,該有Exception 就會有 Exception...)
  • 寫完後,可以直接按 "測試總管" 的 "全部執行",就會知道是否通過單元測試了! 在測試的過程中,再回去改程式,我覺得這個過程是最大的收穫 !

3. 要如何確認自己做夠多的單元測試? => 計算程式碼涵蓋範圍
  • 還記得剛才的XP Test Rule "所有程式碼都要有單元測試 !",所以要如何判斷自己有涵蓋夠多的程式? 你可以使用Visual Studio 2012的  "分析程式碼涵蓋範圍"功能 ,馬上就可以看到涵蓋結果。至於你的目標要設為多少,可能要由你的 developer team 自行在definition of done 中定義。 
  • 如果你有引用Web Service 或 WCF,預設在計算程式碼涵蓋範圍時,會把這些外部參考也會當作範圍,這樣分母就會很大,涵蓋率就會變低,看了就不爽! 這時你可以自己加上一個設定檔(參考這一篇 Customizing Code Coverage Analysis ,最下方有範例檔案),並且在裡面增加排除Web Service/WCF,這樣就不會把外部的算進去了! 想要直接下載的可以到這邊下載,加到你的測試專案中,並在 "測試"->"測試設定"->"選取測試執行檔"使用這設定。
    • 加入後設定檔案後,要自己指定


    • 設定檔案中,如果不想納入Web Service/WCF,這邊要加上排外的設定。設定完再執行一次 "分析程式碼涵蓋範圍" 就會看到變化囉 !



4. 要如何準備單元測試的多筆測試資料? => 資料驅動的測試方式
  • 如果你的developer team有專職的測試人員或是你的Product Owner/Customer 願意準備測試資料,這時你就可以使用 資料驅動的測試方式 (Data-driven test) 。這方式可以用 CSV, EXCEL, Database 當作資料來源,而在單元測試時,就可以取得這些資料,進行多次的測試,太棒啦,測試可也切成資料與邏輯 !  
  • 使用 data-driven 測試有幾個眉角需要注意的
    • (1)資料上,準備時要有輸入的參數,還有預期的結果,譬如大家都熟悉的 Excel ,我有一個function要驗證帳號密碼是否正確,所以有3個欄位:帳號,密碼,預期結果,類似下圖

    • (2)資料上,檔案要自己加入倒測試專案中,並且在 "複製到輸出目錄" 要選 "永遠複製"
    • (3)程式上,要自己加上 using System.Data 
    • (4)程式上,要加上 private TestContext testContextInstance; 的一段
    • (5)程式上,要加上 Data Source,Connection String 可以參考這篇 Data Driven Testing with Visual Studio 2012 – Coded UI Test ,已經整理好了,改改檔名就可以。
    • 這段的程式是這樣的,其實很單純,但是  Visual Studio 2012 就是不會自己做.


5. 要如何把自己的 Function 跟別的 Interface/Library 隔離?  => Fake 機制
  • 有時候測試的時候會希望跟別的 Interface/Library 切割的比較清楚,譬如拿到輸入資料後不寫入資料庫,只要判斷程式邏輯正確,這時要怎麼切割呢? 在 Visual Studio 2012 時加上了 Fake 的機制,請一定要參考這一篇 VS 2012 的 Unit Test與測試總管 的"建立 Fake 組件"  與這一篇 Isolating Code Under Test with Microsoft Fakes ,Fake機制裡面還可以分為 Stub 與 Shim ,各有各的適用對象。以下小弟舉一個簡單的範例,使用的是Shim給大家參考:我要做的是,不去資料庫檢查帳密,我只要確定帳號密碼不一致就可以,所以我用Shim 隔離我的測試程式與實際的程式。


6. 要如何自動Build 自動單元測試?  => TFS 現在5人內免費!
  • 如果你是 one person team (獵人的磊扎,就是下圖那一位,1個人可以當5個人),那你可以不用考慮這個了 "持續整合/持續集成"這個議題,你自己應該會持續的 build,並且你的開發環境上就是 releasable build...

  • 如果你不是 one person team,一定會遇到版本控制、自動整合Build、自動測試的議題。在這部分,我是使用免費的tfs,現在是5人以內免費,適合small team (M$可能不知道台灣軟體廠商 >5個工程師的專案是很大的專案 哈...)。這一段的內容我是參考這一本書 軟體測試實戰 Visual Studio & Team Foundation Server (推! 雖然是visual studio 2010 ,但是還是很有參考價值,不知道會不會改版..) 書中的 "10-03 自動化組件與測試" 。使用方式大致是這樣:
    • (1) Check in 最新的程式版本到 tfs,Solution 應該也包含測試的專案!
    • (2)建立新的 組建定義,設定一下組建方式等等,譬如指定星期一到五中午12:30 tfs自己build自己測試...
    • (3)每天跑完後回去看結果,因為組建設定中,已經包含執行自動測試,所以在build完成後,tfs也會測試,測試的結果也在報告中,下圖是我前幾天的測試結果,可以看到單元測試錯誤! 



後記
  前前後後約花了約 1個月在Survey、熟悉這些工具的操作,並且在目前開發的系統加入單元測試。在開始加入單元測試後,發現了一些自己的盲點 (function執行的結果與預期不一樣),修改後也增加了程式的 robustness, Unit Test 好物!


參考資料

沒有留言:

張貼留言