爲何要有這篇文章呢?數據庫
我的在不斷實踐中愈加以爲,單元測試對於代碼質量的保障真的太有意義了,至少能體如今以下兩個方面:設計模式
①讓你寫出更好的代碼,可測試的代碼必定是優雅的代碼(爲了可測試,你必需要解耦,必需要遵循較好的設計模式)網絡
②讓你重構之類的操做更加放心,由於測試會告訴你影響了哪些功能點單元測試
因此想簡單寫寫關於單元測試的一些基礎相關東西測試
單元測試是什麼?spa
也不想上網找定義,就說說大概本身的幾點理解:設計
單元測試是單元的,即只是對一個系統裏某個特定模塊(或者方法)有限條件下(好比某個if分支)的測試code
單元測試不該該依賴任何外部系統(如網絡/數據庫甚至是時間)全部依賴都應該經過依賴注入的形式獲取blog
單元測試是能夠重放的,一次成功後任意狀況重試應該也老是成功的string
舉個栗子
爲了說明白單元測試,下面寫一個簡單的代碼
首先他的功能很簡單,返回當天是什麼時刻(上午/下午之類的),具體需求就是
0到6點返回「晚上」 6到12點返回「上午」 12到18點返回」下午」 18點到24點顯示」傍晚」(別糾結誰家傍晚還能到24點這種細節了)
此時咱們可能會寫出以下代碼:
1 public string 當天時刻() 2 { 3 DateTime time = DateTime.Now; 4 if(time.Hour >= 0 && time.Hour < 6) 5 { 6 return "晚上"; 7 } 8 if(time.Hour >= 6 && time.Hour < 12) 9 { 10 return "上午"; 11 } 12 if(time.Hour >= 12 && time.Hour < 18) 13 { 14 return "下午" 15 } 16 return "傍晚"; 17 }
上面這段代碼,從功能角度來講,是完美的,他完美的實現了需求。
可是從測試的角度來講,他是災難性的,由於這個代碼不可測試。
爲何這麼說呢?
如今電腦時間是上午9點,我運行這個程序,我預期他返回是「上午」。
好了,測試經過,而後另外一個小夥伴可能他電腦時鐘設置有問題也同一時刻運行卻返回了「傍晚」
或者說我在下午的時候運行他返回了給我「下午」
在這裏,這個程序執行的結果不肯定,他會受到電腦時鐘的影響。
爲何這個代碼不能夠測試呢?
咱們來分析下這個代碼,最重要的一點就是
DateTime time = DateTime.Now;
這句話是獲取電腦當前的時間。
咱們須要的功能是,告訴我如今是什麼時刻,而後這段代碼裏雜合了獲取時間的這麼一個非需求內的功能(違反了單一職責)
另外」獲取時間」由於是受到外部條件控制(電腦時鐘),而這裏明確的直接使用了DateTime.Now,因此也可能違反了依賴倒置原則
如何解決呢?
其實並不複雜,咱們只要將方法內獲取時間改成經過參數的形式傳遞進來好了
1 public string 當天時刻(DateTime time) 2 { 3 if(time.Hour >= 0 && time.Hour < 6) 4 { 5 return "晚上"; 6 } 7 if(time.Hour >= 6 && time.Hour < 12) 8 { 9 return "上午"; 10 } 11 if(time.Hour >= 12 && time.Hour < 18) 12 { 13 return "下午" 14 } 15 return "傍晚"; 16 } 17
這樣作意味着什麼呢?
意味着將時間的獲取交給外部去處理,而方法內將只專一於處理「獲取當天時刻」相關的主線邏輯,遵循單一職責
此時若是我要對這個方法進行測試的話就能夠寫出以下單元測試用例,且下述用例永遠不會受到外部條件影響,只要」當天時刻」這個方法不出bug他永遠該是對的就是對的
1 //簡化代碼,假設當天時刻是當前類裏的靜態方法,假設使用了Shouldly類庫來作Assert 2 [Fact] 3 public void 上午九點_應該爲上午() 4 { 5 當天時刻(new DateTime(2018,1,1,9,0,0)).ShouldBe("上午") 6 } 7 8 [Fact] 9 public void 下午三點_應該爲下午() 10 { 11 當天時刻(new DateTime(2018,1,1,15,0,0)).ShouldBe("下午") 12 }
有了這個單元測試後,往後是否是想重構「當天時刻」這個方法也多了個保障,由於一旦你改錯了,單元測試會誠實告訴你改出問題了
並且經過將代碼改成「可測試」的,也將代碼的優雅程度提升,使其遵循了單一職責,而且避免了違反依賴倒置原則
另外這個故事告訴咱們,小手一抖,就能違反n個原則。。。(隔壁家:不就改了個DateTime.Now嘛,怎麼就搞出那麼多有的沒的)