三问TDD: 单元测试总是好的吗?
生活随笔
收集整理的這篇文章主要介紹了
三问TDD: 单元测试总是好的吗?
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
原帖:再問TDD: 擴散角模型
有關測試“后行”也可以接受的說法,說明了一個事實:即使是最中堅的測試粉絲,也經常需要修正自我。很多理論拋出來之后,在現實面前,都不斷的妥協。一些妥協到基本完善,一些妥協到基本完蛋。
說實話,我表面上是一個保守派,其實骨子里是個激進派。幾年前我的想法深度和廣度都不足,實際上也非常容易跟潮流; 現在情況稍微好了一點(我只敢這么說),所以我的激進反而表現在,無論你多牛的人提出,再多的人捧場,只要讓我覺得不舒服,同時這種不舒服是存在道理支撐的,我就會比倡導者更早的進入砍掉多余內容或增加判定條件的過程。 如果我說我對流行趨勢不屑一顧,那我太張狂了,也不是實情。實際情況是,我關注它們能帶來的東西,但也確實持謹慎態度,同時或多或少的能感覺到一點它們必將發生的修正道路。
關于先行還是后行(雖然后行已然很難說是TDD,同時失去了先行測試的一些重要優勢),其實我在乎的不是這個。存在很多情況,后行會比較舒服,這是必然的。但是關于“必須”這個詞,我覺得還是太絕對了。我們都不必考慮單元測試對靜態語言到底發揮多大作用,也不必去注意單元測試的最大受益者是動態語言這一事實,只要再考慮一下我說的“擴散角”模型就夠了。
在很多情況下,一個隱含BUG但能以95%的概率正確完成任務的東西,比因為抱有單元測試在各個粒度級別必須達到一定覆蓋的想法,而超過了成本能承擔的極限所造成的問題,還是要好得多。 反過來,另一些情況下,一個隱含著定時炸彈的程序是相當危險的,比如醫療方面的嵌入式軟件。 雖然軟件和參與人員的規模本身就會對流程和組織結構的進行產生重大影響,但是如上所述的基本道理,仍然是在何種程度上確立測試的標準和規模的重要因素。
如果讓我倡導測試理念,我覺得除了掌握好測試的切入點和目標的粒度,一個首先要學會的東西,就是在列表里劃出優先級,同時標出不可獲缺的測試,和那些如果砍掉對置信度影響極其輕微的測試。 另外一方面,排除那些“先行測試”的重大優勢,具體到可以“后行”的單元測試,我認為單元測試的必要性,很多時候來自于開發者腦中對該單元的不確定性。有些時候,直接抹掉不確定性的成本要高于使用測試的成本,這時候測試就是必要的;而再另一些時候,測試不過是對腦海中確定的東西換一種方式重復一遍罷了。
后者本身是不需要什么單元測試的,但是很多人總在強調一件事:是人就會犯錯。但這種說法沒有考慮該事實的另外一個方面:既然是人就會犯錯,當你重復自己的時候,或別人重復相同的知識的時候,也仍然存在犯錯的幾率。這個事實所帶來的結果可能是非常讓人驚詫的:當我們無差別的認為所有目標都應該進行單元測試時,有可能使得結果的置信度被降低; 最終要么造成更大的麻煩,要么反而使得軟件的可靠性受到影響。 這或許有點聳人聽聞,但這就是我為什么強調,增加一個環節,有時候并不只是增加工作量,而是引入更多的混亂的其中一個可能的原因。
所以在這里,我可以很個人的給單元測試增加一個“必要性指標”,這個指標是由測試目標可能存在的不確定性所決定的。對于較大型的組織來說,應該對這事有一個統一的判斷標準,哪怕僅僅是測試目標的預期的代碼行數; 更好的做法是對復雜度建立起統一且規范的判定標準。這樣做甚至對于那些肯定應該進行的測試,也能起到一定的輔助作用(比如認識上的)。
好鋼要使在刀刃上。說到底,無論是測試,還是硬生生的調試,我們關心的不是別的,還是成本的問題: 哪怕一團糟,我們如果有無限的時間,無限的金錢,愛怎么寫根本無所謂。所以當我們說測試是“必須”的時候,或者說測試毫無用處的時候,最好還是先看看表,數數人頭,再摸摸自己的錢包; 也許他們會讓你不得不選擇去寫某些測試,但是對另一些情況和其它一些測試,答案正好相反。另外,如果對負面效果無法做出清晰的認識,我們可以去用,但不要對測試產生依賴性。
總而言之,很多測試倡導者的問題,恰恰不是過于重視測試,而是對待測試的態度過于輕易了。我們無論使用什么手段,實質上追求的僅僅是一個概率,這數字到底是會上升還是下降,是一個問題; 提高每一個百分比所要付出的代價,也還是要精打細算才成。 對每一個可能帶來變化的決定,只有四個字: 如臨大敵,如履薄冰。
另外,我希望大家注意一個倡導了20年的,比測試要輕量的多,甚至更應該作為一種良好習慣的編程技巧,既子程序(Routine)內的斷言。一個測試如果“后行”是可以被允許的,那么也許這個測試可以部分的用斷言代替:當斷言認證了所有造成不確定的點,我們就沒必要在測試中重復這些知識,從而避免引入新的不確定性。 凡是適合使用斷言增加置信度的地方,我個人的看法是,斷言的性價比相對測試要高得多;唯一需要注意的是,斷言在某些場景下也會連帶一些負面的效果,或者成本比測試還要高; 這仍然是對不同手段適用性的判斷。更多的思考是,在稍微大一些的組織內,我們無法確認程序員是不是正確的在子程序內使用了斷言。但在這上面,我覺得,信任程序員比信任測試員好: 對相同的知識,前者有一個產生風險的主體,后者卻有兩個。
說實話,我覺得有些大牛和他們的文章都有點,怎么說呢? 不夠嚴肅,或者說不夠嚴謹。 實際上建立簡單的模型和進行基本的分析,對一些要素的覆蓋達到一定比例,就可以獲得更好的指導原則;但是我要說的是,他們在宣傳時忽略了這些工作,或者說對這些方面沒有很好的展開;更沒有盡職盡責的把這些表面之下的工作結果,推廣到受眾群里去。一些人可能是沒時間,也沒這個義務;但另一些人,就像我說MS隱藏ASP.NET的復雜性一樣,是故意的。
我覺得這才是我這類嘩眾取寵之人存在的最大的必要性:做一個提醒。但是我這類人也絕不可能花幾個月時間,進行分析研究工作,然后拿出一個嚴謹的,令人信服的行動的初步指南。這些工作還是必須大家自己去做,尤其是某一理念的追隨者們需要付出更多。
最后說明,如果場景要求或者規定非測試不可,我更多的傾向于“先行測試”;主要是因為“先行測試”不僅僅帶來提高置信度的好處: 在很多情況下,這些額外的東西才是TDD的理念或者比如使用測試的方法如敏捷所關心的首要問題。另外,我不太想探討測試或具體某種測試的必要性;我真正想強調的是,我們需要更多的擁有足夠判斷力的人,才可以做出更多的正確的決定,讓從行業到具體組織內部的軟件構件水平,上升一個臺階。
Update:
附錄:
取自Venus神廟的小扯一下調試。。。后半段。紅色為怪怪的注釋。
到調試器,不得不說測試(這里指的是程序員個人的單元測試...)。在此前一點概念需要說明。在很多人概念里,調試簡單的對應使用調試器,與測試是格格 不入的。而在本書中,調試,指為了發現錯誤的起因所使用的所有手段,其中包含測試。測試最最最最優良的一點在于它是自動化的(這是個要點)。或者說是可永遠重現的(這是付出好幾方面的代價交換來的,因為測試也是人手工編寫的)。這就 是說,使用測試,可以幫你解決誘發錯誤,重現錯誤等及其麻煩的事情。但是,測試往往只是能夠幫你找到錯誤的出現點,而不總是能快速的把你帶到錯誤的起因 點,而為了部署這些測試你需要花費很多的精力。正是基于此,包括云風老大(此人真是讓人又愛又恨...,從這方面來說和linus有點像)等很多人不屑使用測試(再次強調,這是指個人的單元測試...)。但是,這部以為這測試不重要,而成為你可以肆意偷懶放低代碼質量的借口。而是因為其帶來的好處被其他的一些技術抵消了(也可以被非技術因素抵消),這樣的話,為其付出的代價就會顯得很沉重。
但是無論你使用什么樣的手段進行調試。代碼結構的質量才是最最最根本的內容。一個函數劃分很細致,不大量濫用全局變量的代碼,使用很多無副作用的函數,通 過測試你發現錯誤出現點,往往你就能很快到達錯誤起點(因為你把錯誤可能出現的地方限定在了有限的范圍)。而如果你代碼結構混沌不堪,函數/類之間關系錯 綜復雜,哪怕你用測試找到了錯誤出現點,開著調試器進去看,你也會很快轉暈。所以提高代碼質量,合理應用適合的調試工具和手段,正確使用調試的方法才是正 道。在這里,不得不提一下TDD。最初的時候我總覺得TDD最核心的是T,Test。后來才開始明白,它最核心的其實是D,Drive(我非常認同,這就是我說的測試之外的東西)。你可以把測試寫的 很弱,但你一定要在此影響下把代碼重構的很好。由此得到一個蠻歪的理:如果你或你團隊的代碼素質很高,可以嘗試不用TDD的一些開發手段(不認同簡單的以人的水平決定,而是如我正文所說,按照一定的條件作出判斷);但如果你或你團 隊代碼素質不夠高,請把自己套在TDD里面磨練一下。之所以說歪,是因為我其實只想說后半句,前半句純屬為了對仗工整而用。
最后我發現我忘提一個很好玩的東西,斷言。我一直覺得這個東西很有意思。是一種游離在單元測試與調試器之外的手段,是一個隱藏在事后分析中的事前推測派來 的間諜。assert的意義在于猜測并提醒一些最有可能的錯誤,它不精確但也不死板。我一直覺得,全面合理的鋪下斷言這張網(三個部分,輸入輸出不變 式),可以很有效的提高調試速度。(呵呵,對這段沒啥可說的,支持)
有關測試“后行”也可以接受的說法,說明了一個事實:即使是最中堅的測試粉絲,也經常需要修正自我。很多理論拋出來之后,在現實面前,都不斷的妥協。一些妥協到基本完善,一些妥協到基本完蛋。
說實話,我表面上是一個保守派,其實骨子里是個激進派。幾年前我的想法深度和廣度都不足,實際上也非常容易跟潮流; 現在情況稍微好了一點(我只敢這么說),所以我的激進反而表現在,無論你多牛的人提出,再多的人捧場,只要讓我覺得不舒服,同時這種不舒服是存在道理支撐的,我就會比倡導者更早的進入砍掉多余內容或增加判定條件的過程。 如果我說我對流行趨勢不屑一顧,那我太張狂了,也不是實情。實際情況是,我關注它們能帶來的東西,但也確實持謹慎態度,同時或多或少的能感覺到一點它們必將發生的修正道路。
關于先行還是后行(雖然后行已然很難說是TDD,同時失去了先行測試的一些重要優勢),其實我在乎的不是這個。存在很多情況,后行會比較舒服,這是必然的。但是關于“必須”這個詞,我覺得還是太絕對了。我們都不必考慮單元測試對靜態語言到底發揮多大作用,也不必去注意單元測試的最大受益者是動態語言這一事實,只要再考慮一下我說的“擴散角”模型就夠了。
在很多情況下,一個隱含BUG但能以95%的概率正確完成任務的東西,比因為抱有單元測試在各個粒度級別必須達到一定覆蓋的想法,而超過了成本能承擔的極限所造成的問題,還是要好得多。 反過來,另一些情況下,一個隱含著定時炸彈的程序是相當危險的,比如醫療方面的嵌入式軟件。 雖然軟件和參與人員的規模本身就會對流程和組織結構的進行產生重大影響,但是如上所述的基本道理,仍然是在何種程度上確立測試的標準和規模的重要因素。
如果讓我倡導測試理念,我覺得除了掌握好測試的切入點和目標的粒度,一個首先要學會的東西,就是在列表里劃出優先級,同時標出不可獲缺的測試,和那些如果砍掉對置信度影響極其輕微的測試。 另外一方面,排除那些“先行測試”的重大優勢,具體到可以“后行”的單元測試,我認為單元測試的必要性,很多時候來自于開發者腦中對該單元的不確定性。有些時候,直接抹掉不確定性的成本要高于使用測試的成本,這時候測試就是必要的;而再另一些時候,測試不過是對腦海中確定的東西換一種方式重復一遍罷了。
后者本身是不需要什么單元測試的,但是很多人總在強調一件事:是人就會犯錯。但這種說法沒有考慮該事實的另外一個方面:既然是人就會犯錯,當你重復自己的時候,或別人重復相同的知識的時候,也仍然存在犯錯的幾率。這個事實所帶來的結果可能是非常讓人驚詫的:當我們無差別的認為所有目標都應該進行單元測試時,有可能使得結果的置信度被降低; 最終要么造成更大的麻煩,要么反而使得軟件的可靠性受到影響。 這或許有點聳人聽聞,但這就是我為什么強調,增加一個環節,有時候并不只是增加工作量,而是引入更多的混亂的其中一個可能的原因。
所以在這里,我可以很個人的給單元測試增加一個“必要性指標”,這個指標是由測試目標可能存在的不確定性所決定的。對于較大型的組織來說,應該對這事有一個統一的判斷標準,哪怕僅僅是測試目標的預期的代碼行數; 更好的做法是對復雜度建立起統一且規范的判定標準。這樣做甚至對于那些肯定應該進行的測試,也能起到一定的輔助作用(比如認識上的)。
好鋼要使在刀刃上。說到底,無論是測試,還是硬生生的調試,我們關心的不是別的,還是成本的問題: 哪怕一團糟,我們如果有無限的時間,無限的金錢,愛怎么寫根本無所謂。所以當我們說測試是“必須”的時候,或者說測試毫無用處的時候,最好還是先看看表,數數人頭,再摸摸自己的錢包; 也許他們會讓你不得不選擇去寫某些測試,但是對另一些情況和其它一些測試,答案正好相反。另外,如果對負面效果無法做出清晰的認識,我們可以去用,但不要對測試產生依賴性。
總而言之,很多測試倡導者的問題,恰恰不是過于重視測試,而是對待測試的態度過于輕易了。我們無論使用什么手段,實質上追求的僅僅是一個概率,這數字到底是會上升還是下降,是一個問題; 提高每一個百分比所要付出的代價,也還是要精打細算才成。 對每一個可能帶來變化的決定,只有四個字: 如臨大敵,如履薄冰。
另外,我希望大家注意一個倡導了20年的,比測試要輕量的多,甚至更應該作為一種良好習慣的編程技巧,既子程序(Routine)內的斷言。一個測試如果“后行”是可以被允許的,那么也許這個測試可以部分的用斷言代替:當斷言認證了所有造成不確定的點,我們就沒必要在測試中重復這些知識,從而避免引入新的不確定性。 凡是適合使用斷言增加置信度的地方,我個人的看法是,斷言的性價比相對測試要高得多;唯一需要注意的是,斷言在某些場景下也會連帶一些負面的效果,或者成本比測試還要高; 這仍然是對不同手段適用性的判斷。更多的思考是,在稍微大一些的組織內,我們無法確認程序員是不是正確的在子程序內使用了斷言。但在這上面,我覺得,信任程序員比信任測試員好: 對相同的知識,前者有一個產生風險的主體,后者卻有兩個。
說實話,我覺得有些大牛和他們的文章都有點,怎么說呢? 不夠嚴肅,或者說不夠嚴謹。 實際上建立簡單的模型和進行基本的分析,對一些要素的覆蓋達到一定比例,就可以獲得更好的指導原則;但是我要說的是,他們在宣傳時忽略了這些工作,或者說對這些方面沒有很好的展開;更沒有盡職盡責的把這些表面之下的工作結果,推廣到受眾群里去。一些人可能是沒時間,也沒這個義務;但另一些人,就像我說MS隱藏ASP.NET的復雜性一樣,是故意的。
我覺得這才是我這類嘩眾取寵之人存在的最大的必要性:做一個提醒。但是我這類人也絕不可能花幾個月時間,進行分析研究工作,然后拿出一個嚴謹的,令人信服的行動的初步指南。這些工作還是必須大家自己去做,尤其是某一理念的追隨者們需要付出更多。
最后說明,如果場景要求或者規定非測試不可,我更多的傾向于“先行測試”;主要是因為“先行測試”不僅僅帶來提高置信度的好處: 在很多情況下,這些額外的東西才是TDD的理念或者比如使用測試的方法如敏捷所關心的首要問題。另外,我不太想探討測試或具體某種測試的必要性;我真正想強調的是,我們需要更多的擁有足夠判斷力的人,才可以做出更多的正確的決定,讓從行業到具體組織內部的軟件構件水平,上升一個臺階。
Update:
附錄:
取自Venus神廟的小扯一下調試。。。后半段。紅色為怪怪的注釋。
到調試器,不得不說測試(這里指的是程序員個人的單元測試...)。在此前一點概念需要說明。在很多人概念里,調試簡單的對應使用調試器,與測試是格格 不入的。而在本書中,調試,指為了發現錯誤的起因所使用的所有手段,其中包含測試。測試最最最最優良的一點在于它是自動化的(這是個要點)。或者說是可永遠重現的(這是付出好幾方面的代價交換來的,因為測試也是人手工編寫的)。這就 是說,使用測試,可以幫你解決誘發錯誤,重現錯誤等及其麻煩的事情。但是,測試往往只是能夠幫你找到錯誤的出現點,而不總是能快速的把你帶到錯誤的起因 點,而為了部署這些測試你需要花費很多的精力。正是基于此,包括云風老大(此人真是讓人又愛又恨...,從這方面來說和linus有點像)等很多人不屑使用測試(再次強調,這是指個人的單元測試...)。但是,這部以為這測試不重要,而成為你可以肆意偷懶放低代碼質量的借口。而是因為其帶來的好處被其他的一些技術抵消了(也可以被非技術因素抵消),這樣的話,為其付出的代價就會顯得很沉重。
但是無論你使用什么樣的手段進行調試。代碼結構的質量才是最最最根本的內容。一個函數劃分很細致,不大量濫用全局變量的代碼,使用很多無副作用的函數,通 過測試你發現錯誤出現點,往往你就能很快到達錯誤起點(因為你把錯誤可能出現的地方限定在了有限的范圍)。而如果你代碼結構混沌不堪,函數/類之間關系錯 綜復雜,哪怕你用測試找到了錯誤出現點,開著調試器進去看,你也會很快轉暈。所以提高代碼質量,合理應用適合的調試工具和手段,正確使用調試的方法才是正 道。在這里,不得不提一下TDD。最初的時候我總覺得TDD最核心的是T,Test。后來才開始明白,它最核心的其實是D,Drive(我非常認同,這就是我說的測試之外的東西)。你可以把測試寫的 很弱,但你一定要在此影響下把代碼重構的很好。由此得到一個蠻歪的理:如果你或你團隊的代碼素質很高,可以嘗試不用TDD的一些開發手段(不認同簡單的以人的水平決定,而是如我正文所說,按照一定的條件作出判斷);但如果你或你團 隊代碼素質不夠高,請把自己套在TDD里面磨練一下。之所以說歪,是因為我其實只想說后半句,前半句純屬為了對仗工整而用。
最后我發現我忘提一個很好玩的東西,斷言。我一直覺得這個東西很有意思。是一種游離在單元測試與調試器之外的手段,是一個隱藏在事后分析中的事前推測派來 的間諜。assert的意義在于猜測并提醒一些最有可能的錯誤,它不精確但也不死板。我一直覺得,全面合理的鋪下斷言這張網(三個部分,輸入輸出不變 式),可以很有效的提高調試速度。(呵呵,對這段沒啥可說的,支持)
總結
以上是生活随笔為你收集整理的三问TDD: 单元测试总是好的吗?的全部內容,希望文章能夠幫你解決所遇到的問題。