測試驅動開發實踐 - Test-Driven Development(轉)

一.前言html

不知道你們有沒聽過「測試先行的開發」這一說法,做爲一種開發實踐,在過去進行開發時,通常是先開發用戶界面或者是類,而後再在此基礎上編寫測試。程序員

但在TDD中,首先是進行測試用例的編寫,而後再進行類或者用戶界面的開發。因爲要先開發測試用例,那麼開發人員就必須清楚測試的目的,所測功能模塊的業務邏輯以及須要測試的場景。編程

這樣TDD確保了項目的代碼與所需的業務是匹配的,而且在往後的開發工做中也能確保以前所作的功能的可測試性。設計模式

不少同窗問TDD是使用那種編程語言,或者是某種技術,這裏須要明確的是,TDD並非某種技術,而是一種項目實踐。架構

 

導語:框架

 

傳統開發模式與TDD開發模式的區別在哪裏?TDD開發的困難之處和優勢是什麼?TDD具體開發過程當中又須要用到哪些技術知識點?且看本文做者經過實例來爲你闡述TDD的開發流程,讓你對TDD有一個大體的瞭解。編程語言

 

二.傳統開發模式與TDD開發模式函數

1. 傳統開發模式流程:單元測試

項目代碼開發 -> 編寫測試用例 –> 運行測試用例 -> 修復代碼BUG學習

 

 

2. TDD開發模式流程

編寫測試用例 -> 運行測試用例 –> 編寫項目代碼 -> 運行測試用例 -> 重構代碼

 

 

三.TDD入門難題

說到寫測試用例,通常的同窗都以爲沒啥問題,就根據已有的代碼,順着葫蘆摸瓜的就把該測試的方法都照着「套」一遍,測試有哪些結果也得順着項目的代碼來。

至於這測試代碼能不能測出項目的問題,那是另外的問題,關鍵是測試代碼要Pass,那個人工做才能算完。

可是要在還沒項目代碼以前寫測試用例,那就是等於我要憑空想象那些個抽象又晦澀難懂的功能點,還得要在心中勾勒出它們的輪廓以及細節,這不等於讓我本身畫個餅來充飢麼?問題是我連這餅應該是啥樣子都還沒個譜,這是何等的悲涼啊,要是「藍胖子」在我身邊就行了~

 

 

 

四.TDD的優勢

1. 保證代碼質量,鼓勵開發人員僅編寫知足需求的代碼。

「李雷」同窗號稱馬虎王,常常各類物品丟失,好比女朋友(對象)丟失;借用物品(引用)丟失;使用「IO自來水」以後不關閥門;去倉庫(Database)取貨,由於忘記某些物品,不得不頻繁往返等等。

「韓梅梅」同窗功力深厚,精心打造出了一段瑞士軍刀般的代碼,耗時5天(其實此功能客戶無擴展需求僅要求1天時間完成)。

而在TDD實踐中,咱們須要注重代碼質量,並編寫恰好適量的代碼。

 

2. 保證代碼與業務需求的一致性

通常來說程序員都願意把功能完美的體如今代碼上,可有時候天不隨人意,內心免不得擔心,我這代碼能知足業務需求麼?但在TDD中,首先是進行測試用例的編寫,而後再進行類或者用戶界面的開發。因爲要先開發測試用例,那麼開發人員就必須清楚測試的目的,這樣TDD確保了項目的代碼與所需的業務是匹配的。

 

3. 建立簡明有針對性的接口

一日,「李雷」接到「韓梅梅」發來的爲某個功能準備的闖關寶典和核心步驟(類庫與接口)。可讀了3000遍仍是沒有能理解,一方面是「韓梅梅」採用了古代文言文與現代拉丁語的混搭來書寫核心步驟,另外一方面「韓梅梅」的「韓」式1到1000000的命名規則讓「李雷」在讀了30秒核心步驟後,已經不知道第幾條是第幾條,。

在TDD實踐中,咱們要注重建立有意義的、簡明的接口,由於這一點在與他人合做中尤爲重要。

 

4. 與用戶溝通,明確需求

在開發代碼的過程當中,咱們總會有遇到不太明確的需求點,這個時候和需求人員溝通那是必不可少的,瞭解了功能的輸入和輸出才能保證完美的完成任務。在溝通的過程當中也加深了與客戶的信任和默契度,不知不覺中還能提升EQ,一箭雙鵰。

 

5. 迴歸測試,確保新的更改不影響現有功能

在「韓梅梅」同窗開發某個功能3個月後,「李雷」接到上級指示,客戶要擴展該功能,可是原有功能保持不變。在苦心操勞了以後,「李雷」同窗光榮的完成了任務,正準備接受你們讚譽時,「韓梅梅」跳出來向你們訴苦,那就是「李雷」爲了作擴展功能把她以前作的功能給弄壞了,當時「李雷」那個心啊,拔涼拔涼的!

TDD的開發中加入了迴歸測試,這樣就確保了以前的功能的正確與完整性,減小沒必要要的問題。

 

6. 提高系統的開放性和擴展性

一直以來咱們作事都要講前後順序,軟件開發也有着相似的工序。「李雷」和「韓梅梅」被一塊兒「充軍」到某緊急功能模塊上,而且「李雷」要等「韓梅梅」完成她的功能模塊才能開始本身的模塊。爲了解決這個問題,項目組決定使用某些技術來解除他們的依賴關係,好比使用到IOC以及一些設計模式,讓他們可以同時開發,以後再將兩人的功能模塊組裝到一塊兒。

 

五.TDD開發中須要使用到的技術知識點單元測試、依賴注入框架和模擬對象

1. TDD的工做流

TDD的工做流常常被描述爲「紅燈 -> 綠燈 -> 重構」:首先以一個未能經過的測試開始,隨後編寫足以經過該測試的代碼,而後再重構代碼。固然咱們都不肯意看到不能經過的測試CASE,當你再繼續編寫項目代碼,讓本來不能經過的測試CASE經過的時候,你會感受內心有一絲絲的愜意,而後再將代碼優化重構,瞬間又有了些成就感。抿一口水,工做就這麼快樂的完成了。

 

2. 僞對象、依賴注入框(DI/IOC)與模擬框架

就最簡單的實踐來講,比較常見的三層架構,UI層去調用業務邏輯層,業務邏輯層去調用數據持久層。

「韓梅梅」作業務層的代碼,「李雷」作數據層的代碼,因而乎「韓梅梅」變成了「黃世仁」,「李雷」就成了「楊白勞」,其中辛酸只有「李雷」知道!爲了改變命運,「李雷」決定作個「假」的數據層對象(模擬對象)給「韓梅梅」用着,省的她天天都在那催命。

 

僞對象是對代替外部資源的簡單模擬,它一般會在調用一個方法時爲該方法返回預約義響應,但一般不會根據輸入參數而改變響應。

因而乎「李雷」歡樂的開始了他的計劃,把「韓梅梅」所須要的功能點都用接口來實現(interface),而後把這接口的方法在單獨的一個模擬類裏面都只寫了個簡單的殼,裏面的各類返回值都寫成「韓梅梅」想要的數據樣例,最後語重心長的對「韓梅梅」說:「東西拿走喜兒給我留下…」,「韓梅梅」固然是歡快的蹦到了本身的座位上。

控制反轉是對象在被建立的時候,由一個調控系統內全部對象的外界實體將其所依賴的對象的引用傳遞給它。也能夠說,依賴被注入到對象中。因此,控制反轉是,關於一個對象如何獲取他所依賴的對象的引用,這個責任的反轉

「韓梅梅」拿到李雷給的僞對象後,徑直就用了起來,在全部須要僞對象的類裏面都直接用了萬能的「New」關鍵字來實例化這個僞對象,這招兼顧了簡單與實惠,廣大程序員愛好者都愛這麼幹。

可是 「韓梅梅」後來慢慢意識到不太對,我有許多地方都用用到NEW字,那不是之後「李雷」完成了他所謂的真的對象之後,我還必須得改個人代碼,把我以前放進去的僞對象給替換爲真正的對象?我這不是本身給本身找茬麼。

「韓梅梅」趕忙找到帶着黑框身背雙肩包的師兄,細說了當前的苦衷。黑框師兄那捨得是師妹這麼憂愁,趕忙拿出殺手鐗「控制反轉」中的一招「依賴注入」,讓使用類中僅保留被調用對象的接口,而後動態的注入實例給這接口,這樣子只要實現了這個接口的類均可以被任意替換使用,而且這個注入的動做通常是由某個框架來實現的,好比Autofac,、Unity或者Ninject等等。

這下子「韓梅梅」內心踏實了,管你「李雷,張雷,王雷」寫什麼僞對象或者真的對象,只要你的對象實現了指定的接口,我都能使用,並且我還不用本身去手動建立這個對象,省心又省時。

模擬框架是一系列用於快速建立僞對象的API,它能減小重複的代碼,提升編碼效率,比較經常使用的爲Rhino, NSubstitute, Moq等。

 

「李雷」和「韓梅梅」就這麼和諧的合做,可是隨着任務的增多,發現問題來咯:

1)       「李雷」要手工作不少的僞對象給「韓梅梅」,任務繁重。

2)       每一個對象的內部需求是不同的,「李雷」發現要用一種通用的格式來建立這些僞對象幾乎是不可能的。

3)       不少僞對象又依賴於其餘的僞對象,這樣子簡直就是要讓崩潰。

4)       不少爲對象內部有狀態須要保存,手動來寫代碼很難去維護這些狀態標示。

「李雷」好不容易經過建立僞對象來擺脫「韓梅梅」的每日請安,這又掉進了建立無數僞對象的漩渦之中。因而「李雷」橫渡遠洋,登山涉水,指望能找到一盞明燈解決這些問題,終於功夫不負有心人,「李雷」找到了神兵利器去解決這個問題,那就是模擬框架。

模擬框架幫助「李雷」快速的建立了各類「韓梅梅」須要的模擬對象,以及各類所需的API,彈指一揮間,李雷用這神兵利器已經殺敵無數,嘴角不禁得上揚了一番!

 

3. 重構代碼

可是問題總歸仍是有得,她發現本身有些功能雖然測試經過,可是代碼寫的很差,常常被黑眼圈師兄批評,說她的代碼質量不高。

因此她必須儘量在剛測試經過以後就儘量的優化代碼,一來是少捱罵;二來也是提升本身編碼水平的一個機會,查漏補缺;三是貴人多忘事,況且是「我這等如花似玉的姑娘!」,若是不及早優化,恐怕之後很難有時間再來弄了。

由於已經有了測試代碼,因此重構代碼那也是頗有保障的事情,若是我改錯東西了,那麼我寫的測試用例確定不能經過,這樣子也能讓我信心滿滿的去把這些個有臭味的代碼大卸八塊了。

 

六.工序流程

下面咱們來看看「韓梅梅」和「李雷」他們的工做步驟:

1. 首先,韓梅梅和李雷分析了他們各自的業務,而後韓梅梅寫出了她須要測試用例,裏面嘗試使用「李雷」將要提供的方法,並經過此方法獲取數據。固然這些代碼第一次是測試不經過的,由於裏面須要的實現類尚未寫。這裏咱們使用到Moq這樣一個模擬框架。

 

測試用例的運行結果,你們也是知道的,兩個字「悲催」!

 

 

2. 而後, 「李雷」那邊開始了數據持久層接口的編寫(IProductRepository),「韓梅梅」拿到李雷提供的接口後,完成了業務邏輯層(ProductService)的代碼編寫,完畢以後大吐一口氣:「小夥子終於給力了一次!」。

A.  「李雷」的代碼以下,實際上「李雷」只是提供了接口(interface)給「韓梅梅」,他還並無開始編寫具體的實現類,可是韓梅梅已經能夠經過該接口來工做了。

 

B. 「韓梅梅」的服務類代碼以下,她獲取到「李雷」提供的數據持久層的接口後就開始歡快的編寫代碼,一切是那麼的行雲流水啊:

 

 

3. 接下來「韓梅梅」添加了各類須要的引用,再次運行起了測試用例,此次順利的PASS了,內心那個激動,沒的說!

 

 

 

4. 工做快要接近尾聲,不過眼鏡師兄提醒過「廣大程序猿應該有高度的思想覺悟,竭盡全力的提升代碼質量」,爲了達成這一目標,「韓梅梅」又開始了上跳下竄的「你們來找茬」。

她發現裏面有段代碼寫的很差,循環太多,也不夠整潔,她想優化下代碼,又怕把寫好邏輯弄壞了,不過如今有了測試用例,她不會再怕有這個問題,改錯代碼,測試用例天然也就沒法經過。

   

再運行下測試用例,依然經過,這次代碼優化完畢,若是還有新的問題能夠在依葫蘆畫瓢的繼續優化。     

 

 5. 與此同時,「李雷」那邊的數據持久層代碼也差很少寫好了,你們總得須要把代碼合起來做「集成測試」,這個時候就要用到IOC框架來把「李雷」編寫的數據層實例注入到業務邏輯層,注入實例使用的是Autofac這個IOC框架,咱們這裏使用構造函數注入,關於注入框架的更多信息,請讀者G….gle。

 

 

至此,「韓梅梅」與「李雷」各自的工做都完成了,你們也不在互相說啥,各自都優化了各自的功能代碼,快樂的工做繼續進行着,咱們的TDD講解也到此結束!

 

參考文獻:

  1. Test Driven Development: By Example – Kent beck
  2. Refactoring: Improving the Design of Existing Code – Kent beck
  3. The Art of Unit Testing: With examples in .NET – Roy Osherove
  4. Professional test driven development with C# - James Bender, Jeff McWherter

 

本人最近開始學習TDD,藉此提高本身的能力。在學習過程會有一些心得體會,因而便會寫一些博客來記錄這些想法,有興趣的朋友能夠和我一塊兒交流學習。

QQ羣: 32745894,歡迎你們加入討論!

http://www.cnblogs.com/zhq3051/p/4596049.html

相關文章
相關標籤/搜索