前言
在工作中或者在面試中蚤霞,我經常碰到的開發(fā)人員就是對單元測試不重視,這一類基本上都表現(xiàn)出了一種“無知的自信”昔馋,總覺得自己寫的代碼質量很高五芝,直到一次次蟲子(Bug)把自己咬的頭破血流時,才發(fā)現(xiàn)原來自己的代碼已經到了剪不斷理還亂的狀態(tài)刊咳,而每一次修改一個bug彪见,都需要走一遍“墨鏡迷宮” (看上圖)。還有很多人知道單元測試或者寫出了單元測試娱挨,但是就是寫了一個方法余指,上面標注了一個[Test]屬性而已,甚至很多的人單元測試上面標注的是[IgnoreTest], 每次看見這些跷坝,我都深深的感到推行單元測試之路是艱難的酵镜,是遙遠的碉碉,但是我依然堅信是是渴望也可及的,只要有著深深的信念淮韭,堅強的意志誉裆,無謂的勇氣,一頭扎進去泥巴堆里缸濒,假以時日足丢,當大雨來臨,必將帶走泥巴庇配,從此你拔劍揚眉斩跌,哦,你不用拔劍了捞慌,因為你就是劍耀鸦。。啸澡。
為了讓更多人能夠拔劍揚眉袖订,也為了我們公司剛入職的新人做一些培訓,我精心準備了單元測試的一些知識嗅虏,在此為你奉上洛姑,我盡量用簡短的語句來描述,如果你不清楚我說的某一些點皮服,那么歡迎你發(fā)郵件給我 wangdeshui@outlook.com楞艾,我可以針對集中的點發(fā)篇文章,如果你想知道我說的所有點怎么實踐龄广,那就聯(lián)系我硫眯,試試加入到我們公司來。
什么是單元測試
單元測試是開發(fā)者編寫的一小段代碼两入,用于檢驗被測代碼中的一個很明確的功能是否正確。通常而言敲才,一個單元測試是用于判斷某個特定條件(或者場景)下某個特定函數(shù)的行為裹纳。
執(zhí)行單元測試,是為了證明某段代碼的行為確實和開發(fā)者所期望的一致归斤。因此痊夭,我們所要測試的是規(guī)模很小的、非常獨立的功能片段脏里。通過對所有單獨部分的行為建立起信心她我。然后,才能開始測試整個系統(tǒng)。
為什么要使用單元測試
- 單元測試使工作完成的更輕松
- 單元測試使你的設計更好
- 大大減少花在調試上的時間
- 能幫助你更好的理解代碼
沒有單元測試
- 任何代碼都是在假定其他代碼是正確無誤的情況下編寫的番舆。
- 修改一處代碼時無法得知會對其他代碼產生怎樣的影響酝碳。
- 任何一處改動都需要進行功能級別的整體調試。
單元測試難以推動的原因
太花時間
很多人認為單元測試很花時間恨狈,但是想想我們在下面幾點話的時間疏哗,我經常看到為了測試一個簡單的API方法禾怠,我們很多人必須讓前端跑起來返奉,甚至自己寫一個客戶端才能調用
- 調試上花的時間
- 對自認為正確的代碼,花了多少時間確認代碼是正確的吗氏。
- 定位Bug所耗的時間
測試不是我的工作
很多人認為測試不是自己的工作芽偏,但是想一想每次測試提出一個bug所花的時間,以及你改bug所化的時間弦讽,所以下面2點是很重要
- 內在質量的重要性
- 測試應該是輔助污尉,好的軟件是開發(fā)設計出來的,不是測試出來的
系統(tǒng)可測試性差
- 系統(tǒng)耦合度很高往产,我們需要提高我們的團隊的設計能力被碗。
單元測試最佳實踐
實踐一: 三到五步
- SetUp
- 輸入
- 調用
- 輸出
- TearDown
實踐二: 運行快速
為什么?
單元測試運行很頻繁仿村,是輔助開發(fā)的锐朴,在開發(fā)過程中運行,如果慢影響很大
多快較好奠宜?
- 單個測試小于200ms
- 單個測試套件小于10s
- 整個測試小于10分鐘
實踐三:一致性
任何時候同樣的輸入需要同樣的結果
Date date=new Date()
Random.next()
這樣的代碼都需要Mock掉包颁,不然時間每次都不同,結果就會不一樣压真。
實踐四:原子性
** 所有的測試只有兩種結果:成功和失敗**
不能部分測試通過
實踐五:單一職責
一個測試只驗證一個行為
** 測試行為,不要測試方法 **
- 一個方法蘑险,多個行為 -----> 多個測試
- 一個行為滴肿,多個方法 ----- 一個測試
這里的一個行為,多個方法一般指這個方法調用private, protected, getters, setters - 多個Assert只有在測試同一個行為時可以接受
實踐六:獨立無耦合
單元測試之間無相互調用
- 單元測試執(zhí)行順序無關
- 不同的順序無影響
單元測試之間不能共享狀態(tài)
比如一個測試里設置了一個屬性值佃迄,然后在另外一個測試里用泼差,如果必須共享可以放到Setup里
實踐七:隔離外部調用
- 單元測試需要快速運行,且每次結果一致呵俏,所以需要隔離一切對外部的調用堆缘。
- 不使用具體的其它真實類,就是不要new
- 不讀數(shù)據(jù)庫
- 不讀網絡
- 不讀外部文件
- 適當時候可以構造一個相同的內部文件來Mock
- 不依賴本地時間
- 不依賴環(huán)境變量
實踐八: 自描述
- 單元測試是開發(fā)級文檔
-
單元測試是方法的描述
單元測試自描述命名
實踐九: 單元測試邏輯
- 單元測試必須容易讀和理解的
- 變量名普碎,方法名吼肥,類名
- 無條件語句,無Switch
辦法:分解if到多個測試,所有的輸入都是已知的,所有的結果都是一定的(Mock) - 無循環(huán)語句
- 無異常捕捉
** 測試預知的異常缀皱,用ExpectedException方法 **
實踐十: 斷言
斷言信息最好包含Business Information
斷言信息包含出錯的具體信息如果失敗
-
適當時候可以封裝自己的Assert
比如:Assert.IsProgrammer(Jack) Return Jack. Cancooking() && Jack.CanCoding()
實踐十一:產品代碼
-
產品代碼無測試邏輯
不能有:If(global.IsTest){…}
測試代碼和產品代碼要分離
不要在產品代碼里有任何只供測試用的代碼
使用依賴注入
最后斗这,單元測試常用技術及工具
下面是.NET程序常用的單元測試需要的技術和工具,其它語言請自信比對啤斗。
- 面向接口編程
- 依賴注入(Castle, Unity, Ninject)
- Moq
- 測試工具(xUnit)
- .Net Nunit
- 代碼覆蓋率測試工具Ncover
- 自動運行測試輔助工具NCrunch