測試的目的
軟體測試(Software Testing)是一個大題目,不過其目標都是一樣的,例如,提早發現缺陷,提高軟體品質,產生可靠的程式碼…等。軟體測試的方法論中,測試的範圍可大可小,以程式碼的測試為例,小的單元測試(Unit Testing)、大的整合測試(Integration Testing)。除了對程式碼進行相關測試外,我們還可以對整個網站、資料庫、I/O … 等進行壓力測試。測試可是一門大學問,當然不可能在這小小一篇裡寫完,這一篇只是前言,讓大家對於測試有個初步的瞭解,先有初步瞭解,接下來再介紹 ASP.NET MVC 裡單元測試(Unit Testing)。
手動測試
- 了解需求
- 手動進行基本驗證
例如,我們寫了一段驗證 Email Address 的 Regular Pattern,我們很順的把網頁執行起來,開始在表單的 Email 欄位亂打一通,【abc@abc】、【abc@abc.】、【abc@abc.abc】 … 看不正常資料是否能通過驗證。很多寫的不好的 Regular Expression 到第三個 abc@abc.abc 就 … 過了。這樣對嗎?這就稱【手動測試】,我們日常的 Debug 就常做這種事,但你能瞭解或記得多少組的測試組合呢?
自動化測試
- 介面測試(UI Testing)
- 單元測試(Unit Testing)
- 整合測試(Integration Testing)
簡單說就是透過程式或軟體去進行測試,沒有人的介入去進行測試。舉例來說,字母o與數字0,如果我不要加【字母 | 數字】只單純打【o/0】,你分得出來嗎那個是"ㄛ"那個是"零"嗎?我們常用密碼組合中有一組是 w0rd,透過程式 w0rd 是字串,對就是對,透過人工加老花眼,很容易輸入為 word。自動化測試就是要去除人工介入而可能產生的錯誤,進而提升測試的可信度、可靠度。例如手動測試的例子中,我們的測試資料有一組 abc@abc.c0m (c零m),驗證程式要能正確抓出錯誤才是對的,結果很順手的輸入成 abc@abc.com,然後跑去罵寫這支 Regular Expression 的人,事後又模擬出不來(又輸入了正確的測試資料),然後被冠上"你的程式一定有Bug."的臭名,青天大人這冤不冤?
ASP.NET MVC 單元測試(Unit Testing)
我們開發人員在 ASP.NET MVC 專案通常只做單元測試(Unit Testing),我們注重在 Controller 內的 Action 是否正確無誤,而且進行單元測試(Unit Testing)程式碼的撰寫也可以順便進行 Code review 與 Refactory (非Test-driven development, TDD開發),進行一些物件導向基本原則檢查。
寫單元測試(Unit Testing)就是要讓一切有憑有證有據。讓單元測試(Unit Testing)幫你說話,在《軟體構築美學》在第「4-2-2 為什麼需要測試」一節提到需要測試的幾個原因:
- 確保臭蟲不會反覆出現
- 確保新的程式碼達到可接受的品質水準
- 建立與保持開發團隊和用戶的信心
- 最重要的,加速回饋循環
如果你連單元測試(Unit Testing)都過不了(也許是他人寫,也許是自己寫),那【加速回饋循環】的時間會短的讓你驚訝!
單元測試(Unit Testing)撰寫注意事項
- 測試程式碼不應接觸任何外部資源(External Resources),舉例來說:
- File I/O
- Database Access
- RESTful Service (Web Service/WCF/...)
接觸外部資源的單元測試(Unit Testing)可能造成結果的不信任,以資料庫存取來說,合理的資料庫裡資料是隨時在變動的,對於不斷變動的資料,你要如何測試?另一點是,會讓執行單元測試(Unit Testing)的時間過長,我在《解析极限编程:拥抱变化》的《推薦序》中看到一個數字 Eclipse 這套軟體必須通過21000個單元測試(Unit Testing),這本書的原文是在2004年寫的,現在是2012年,我相信現在的數字會大非常多,你試想這些外部資源都是非常耗時耗資源的,假如每次執行都必須執行21000次 File I/O,我想你會"起瘋"!
就以上理由,ASP.NET MVC 的 Model Class (或 DAL, data access layer) 通常不太寫單元測試(Unit Testing)。以上說明會讓人誤解,比較嚴謹應該改為:如果MVC的Model裡只有"LINQ to SQL類別"或"ADO.NET實體資料模型"或"*.tt"等,實在沒有什麼好寫的。因為那些都是用工具產生的。
- 類別(Class)的靜態物件(例如,VB Shared, C# static)可能受其他方法(Method)而改變狀態(state)。此時無法在單一個方法內驗證其 State 的正確性,在此情況下,就不合適在單元測試(Unit Testing)中驗證靜態物件的 State。反之,執行之後靜態物件會成為"特定 State",就可以寫單元測試(Unit Testing)來驗證。
整合測試(Integration Testing)
針對專案的一部分或全部進行測試。
- 需配合情境設計
- 需先設置測試所需環境
例如,
- 建立測試資料庫
- 建立測試資料
- 建立測試檔案
- 建立測試服務(Web Service / WCF / Web API / ...)
- 執行整合測試
- 刪除所有測試相關資料與服務
單元測試(Unit Testing)是一個點的測試,整合測試(Integration Testing)是面與線的測試。
測試驅動開發(Test-driven development)
以測試來驅動軟體開發。
- 先寫測試程式碼(通常是單元測試)
- 開發被測試的程式碼
- 讓開發的程式碼通過條件(1)的測試程式碼
由於我們必須先寫測試程式碼,開發團隊必須先瞭解需求為前提來寫測試程試碼,然後才進行實際程式碼的開發工作。整體流程大致:
- Test Coding (Unit Testing)
- Test fail
- Developer Coding
- Test Success
- Refactory (Run 3 → 4 → loop )
條件(2)是正常的,因為寫完測試程式碼後,根本就沒有實際程式可以測試,所以一定失敗。有了測試程式碼,接下來就是寫能通過測試程式碼的實際程式碼,以通過測試為前提。如果實際程式需要重構(Refactory)就必須重跑一次條件(3)(4),以確保重構後的程式碼也能通過測試。雖然 TDD 看起來很不錯,但 TDD 還是有先天上的限制,一是需求不清,二是架構不明。不清不明的內容,如何撰寫測試程式碼,即條件(1)都無法執行了,那還談什麼 TDD,對吧。
沒有留言:
張貼留言
感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。