推行TDD的思考

作者: 張逸  發布時間: 2015-04-12 13:56  閱讀: 1878 次  推薦: 5   原文鏈接   [收藏]  

  目前來看,推行TDD的障礙大約有如下幾點:

1. 開發人員的質量意識;
2. 分析需求并進行任務分解的能力;
3. 將測試作為開發起點的開發習慣;
4. 開發人員的重構能力,包括如何識別壞味道和如何運用重構手法;
5. 單元測試的基礎設施,尤其是測試數據準備;

  開發人員的質量意識

  開發人員對于軟件質量,常常偏重于軟件的外部質量,體現在他們的工作效益上,就是被測試人員發現的缺陷數。而慣常的軟件開發思想,總是認為開發人員不適合做測試,因為他們總是站在自己的角度去看待問題,從而可能忽略真正需要測試的用例。這種思想給了開發人員一個錯誤信號,認為自己不應該寫測試,即使寫了測試,也寫不好。殊不知,由開發人員編寫測試帶來的收益,最重要的一點不在于測試本身,而在于它能促進開發、測試以及需求分析人員的交流與溝通。而測試先行的方式也能讓開發者跳出實現的窠臼,而從業務角度去看待問題,從消費者角度去思量接口。此外,由于開發者總是憊懶地將測試職責委派給了專門的測試人員,于是漸漸會產生一種依賴心理。測試人員的精確測試當然可以保障質量,但這種測試通常是黑盒測試,這里保障的質量主要還是外部質量。而且,這種測試帶來的反饋總是慢于開發進度,一旦發現缺陷,修復缺陷的成本也會變得更高。

  軟件質量除了外部質量之外,內部質量同等重要。軟件成本等于開發成本與維護成本之和,而維護成本的增加主要就歸咎于內部質量的糟糕。這里講的內部質量包括:代碼的可讀性、可重用性、可擴展性等。當我們讓開發人員為原有代碼編寫單元測試時,總是覺得舉步維艱。分析原因,主要問題在于代碼的可測試性不好。要測試一個類,竟然連簡單創建它的對象都變成了不可能完成的任務。我們為這樣的代碼編寫單元測試,就好像在觸及蜘蛛網,一旦被這些網絲給牽住,纏住,就可能無法擺脫。除非我們能夠快刀斬亂麻,那對于這個系統而言,就不是維護,而是重寫了。測試先行的開發至少在一定程度規避了這樣的問題。即使代碼的內部質量仍有所欠缺,但在足夠覆蓋率的保護下,我們要進行重構也變得更為簡單。

  然而,這些好處都不是短期能夠見到成效的,且團隊若不能達成共識,只靠一二人堅定地踐行TDD,在測試覆蓋率不夠的情況下,改進仍然有限。多數開發者在維護別人的丑陋代碼時,可能會罵聲連連,殊不知同時作為罵者自身,其實也在重復被罵者的故事。

  我不是說沒有采用TDD,代碼質量就一定不高;但我可以說采用了TDD,代碼質量至少有了可以改進的基礎。

  分析需求并進行任務分解的能力

  需求分析能力常常是開發人員的短板。開發人員養成了一個習慣,看什么事情都會從技術實現的角度去思考。要實現一個網頁,就會想到如何編寫JavaScript來響應用戶的動作,如何編寫CSS,卻不會去思考用戶體驗和操作的流程。要完成一個數據分析,總會想到數據的屬性,轉換和提取數據的算法,卻不會想到分析數據的價值以及合理的流程。

  而且對于繁瑣的需求描述,我們總沒有耐心去深入研讀,而是會在掌握了大體意思后,就開始匆匆進行開發與實現。TDD要求我們在編寫測試之前要做好合理的任務分解。若沒有很好地理解需求,任務分解就無法順利的進行。

  這就帶來了團隊協作的問題。若我們能從需求的源頭上進行改進,或許TDD會變得更容易。例如,我們對故事的拆分更合理,較好地遵循了User Story的INVEST原則,那么我們所要實現的Story在測試性、獨立性方面都會有很好的改觀。如果BA能夠非常明確地編寫出驗收條件(Acceptance Cretiria),進行任務分解就變得更加容易了。

  更進一步,若BA能夠參考甚至遵循Specification By Example,并采用Given-When-Then的模式來描繪各個用例場景,再要進行任務分解,不就變得輕而易舉嗎?因此,有時候推行TDD非常艱難,或許最大的原因是我們僅僅將目光放到了開發者身上,而忽略了BA扮演的關鍵角色。正所謂:問渠那得清如許,為有源頭活水來。

  我一直強調任務分解是有層次的。分析需求時,不能一個猛子就扎進繁瑣的實現細節。要從用戶價值出發,先梳理出最外層的需求任務,然后抽絲剝繭,條分縷析地層層遞進,如此方能理清思路,掌控復雜邏輯。基本上,任務分解可以分為三個層次,即業務價值——>業務功能——>業務實現。并且這個層次是一種“遞歸”的狀態,視需求的復雜度而定。

  將測試作為開發起點的開發習慣

  再說說開發習慣的問題。這種改變顯然不是一朝一夕可以完成的。以我個人的經驗以及我所觀察到的情況來看,固然是習慣的力量在作祟,然而主因還是因為對TDD方法的掌握程度以及一些誤解導致。

  前面已經述及,任務分解應該是TDD的起點。多數開發者未能形成任務分解的習慣。因此在改變為測試先行的時候,錯以為應該一上來就寫測試。因為思路沒有理清,腦子里是一片亂麻,再加上本身對TDD不夠熟悉,于是編寫測試就變得舉步維艱,總覺得束手束腳,就好像被綁了一只手,又好像是在泥沼中掙扎。許多時候,甚至發揮不出自己的哪怕三分的功力。

  一貫以來,我們都在強調測試先行,測試先行……容易產生一種錯覺,就是認為TDD必須一開始就寫測試,“簡單設計”嘛,于是就沒有了設計。這讓那些習慣于事先設計的開發者更難以接受。

  以下是我對于“TDD是否需要事先設計”的個人觀點:

Martin Fowler的文章Is Design Dead?其實就是對此問題的正本清源。我個人認為,視場景而定,測試驅動開發仍可進行事先設計。設計并不僅包含技術層面的設計如對OO思想乃至設計模式的運用,它本身還包括對需求的分析與建模。若不分析需求就開始編寫測試,就好像沒有搞清楚要去的地方,就開始快步前行,最后發現南轅北轍。測試驅動開發提倡的任務分解,實際上就是一種需求的分析。而如何尋找職責,以及識別職責的承擔者則可以視為建模設計。測試驅動更像是一種培養設計專注力的手段,就像冥想者通過盤腿靜坐的手段來體悟天地一樣,測試驅動可以強迫你站在測試的角度(就是使用者的角度)去思考接口,如此才能設計出表現意圖的接口。但編寫測試自身并不能取代設計,正如盤腿靜坐并不等于就是冥想。

在開始測試驅動開發之前,做適度的事先設計,還有利于我們仔細思考技術實現的解決方案。它與測試驅動接口的設計并不相悖。解決方案或許屬于實現層面,若過早思考實現,會干擾我們對接口的判斷;但完全不理會實現,又可能導致設計方向的走偏。舉例來說,如果我們要實現XML消息到Java對象的轉換。一種解決方案是通過jaxb將消息轉換為Java對象,然后再定義轉換映射的Transformer,通過硬編碼或者反射的方式將其轉換為相關的領域對象。然后在執行了業務操作后,再將返回的結果轉換為另一個Jaxb對象。而另一種解決方案則是通過引入模板,例如StringTemplate或者Velocity,定義轉換的模板,然后進行替換實現。這兩種解決方案的區別,直接影響了我們劃分任務的方式。所以在運用TDD時,先不要一巴掌拍死,可以先抱著開放的態度嘗試嘗試。何況,TDD并非一招鮮,吃遍天,總要有適合它的場景。例如UI的開發,交互協作的控制邏輯,數據庫開發,并發處理,都不是運用TDD的太好場景。

  開發人員的重構能力

  TDD的核心是紅——綠——重構。這意味著重構是TDD非常重要的一環,它直接關系到TDD開發出來的代碼質量。沒有好的重構能力,TDD就會有缺失。若說代碼的內部質量是生命的話,重構就是靈魂,缺少了它,代碼就沒有靈性了。多數時候實施TDD,都會因為重構能力的缺乏而陷入困境。

  重構的關鍵首先在于如何識別代碼的壞味道。這需要代碼閱讀的千錘百煉,而非死記硬背老馬總結的壞味道。當這些壞味道變成你的一種直覺,甚至就像與生俱來的一種能力時,你就會降低對糟糕代碼的容忍度。在你眼中,這些爛代碼就是垃圾,必須清掃,否則無法“安居”。

  重構手法與代碼壞味道一一對應。若有測試保障,重構就變得安全。但盡可能地,我們還是希望運用工具提供的自動重構功能,這既提高了重構效率,也在一定程度下確保了重構的安全。

  當然,重要的是要找到重構的節奏感,即小步前行,每次重構必運行測試的良好習慣。若能結合分布式版本管理系統,做到原子提交,就會更加方便。即使重構出現問題,我也可以快速地回到前面的版本快照。

  在TDD過程中,若能結對自然是上佳選擇。當一個人在掌控鍵盤時,另一個人就可以重點關注代碼的可讀性,看看代碼是否散發出臭味。兩個人的眼睛終歸要更銳利一些,至少視野的范圍更廣泛。

  及時重構是重構諸多實踐中最重要的一點。不要讓重構成為你在未來償還債務的殺手锏。越拖到后面,償還債務的成本就越高。以重構而論,可能需要的重構能力就更強,因為重構變得復雜了。當然,只要你的代碼能夠保證足夠的覆蓋率,以及較好的松散耦合,重構依舊可行。采用TDD,基本能滿足這兩條要求。但以成本而論,小步前行才是重構之道。

  單元測試的基礎設施

  最后說說單元測試的基本設施。很多時候,這可能不是問題;但很多時候,這可能會成為大問題。面對諸如測試數據準備等問題,需要認真分析,找到應對方案。原則上最好能找到一些開源的測試框架,包括生成測試數據,模擬測試行為等……多數情況下,這些開源框架都已經提供了。因為你遇到的問題,別人可能早已遇見過。這個世界上有很多聰明而又樂于分享的程序員,不要局限在自己公司一隅。睜大眼睛看看滿世界吧。所謂“君子生非異也,善假于物也”。好程序員,也要這樣。

  說不定,你會拋棄TDD,因為你找到了更好的適合你的做法。

5
0
 
標簽:TDD
 
 

文章列表

arrow
arrow
    全站熱搜

    大師兄 發表在 痞客邦 留言(0) 人氣()