如果無(wú)暇重構(gòu),我們是否應(yīng)該為遺留代碼編寫測(cè)試方案?
譯文編者按:本文主要圍繞著“是否應(yīng)該為遺留代碼編寫測(cè)試方案”進(jìn)行討論,一位同事主張“一次性大規(guī)模重構(gòu)”要比多次小規(guī)模測(cè)試更好的觀點(diǎn),引來(lái)多位網(wǎng)友不同的看法。以下是幾位網(wǎng)友的回答以及得到不同票數(shù)的肯定。歡迎朋友們一起討論!
網(wǎng)友is4發(fā)問(wèn):
在工作中遇到問(wèn)題時(shí),我一般傾向于采取《高效處理遺留代碼》這本書給出的建議。我會(huì)消除代碼依賴性,將一部分代碼轉(zhuǎn)移到VisibleForTesting公共靜態(tài)方法及新類當(dāng)中、從而使這些代碼(或者至少是其中的一部分)具備可測(cè)試條件。另外,我還會(huì)編寫測(cè)試來(lái)確保自己的修改方案或者新功能添加操作不會(huì)影響遺留代碼的固有效果。
但一位同事對(duì)我的作法提出了不同意見。他的觀點(diǎn)是:
• 首先,原始代碼在新環(huán)境下的工作效果可能并不盡如人意。而為其編寫測(cè)試也許會(huì)讓日后的修復(fù)與修改工作變得更為困難,因?yàn)殚_發(fā)人員必須在著手之前先看懂這些測(cè)試本身并對(duì)其加以修改。
• 如果遺留的GUI代碼中包含某種邏輯(例如,~23 lines,2-3 if/else blocks),那么為此編寫測(cè)試實(shí)在無(wú)甚價(jià)值——畢竟這部分代碼的功能根本無(wú)足輕重。
• 類似的糟糕狀況在代碼庫(kù)的其它部分也可能存在(我是個(gè)新人,因此還沒(méi)見過(guò)此類情況);因此通過(guò)一次性大規(guī)模重構(gòu)將它們徹底掃清的作法更輕松些。將邏輯提取出來(lái)能夠消除這部分代碼在未來(lái)引發(fā)麻煩的可能性。
如果我們沒(méi)有足夠的時(shí)間對(duì)遺留代碼進(jìn)行全面重構(gòu),那我是否應(yīng)該放棄提取可測(cè)試部分并為之編寫測(cè)試的作法?這樣做有哪些我沒(méi)有注意到的弊端?請(qǐng)大家提出自己的意見。
感興趣的朋友可以點(diǎn)擊此處查看關(guān)于這個(gè)問(wèn)題的原版表述。
這是種錯(cuò)覺(jué)
網(wǎng)友Killian Foth的回答(獲得79票支持):
我來(lái)說(shuō)說(shuō)自己對(duì)于這個(gè)問(wèn)題的一點(diǎn)不成熟見解:不編寫測(cè)試的理由看起來(lái)似乎廣泛適用,但這其實(shí)只是種錯(cuò)覺(jué)。
首先,沒(méi)錯(cuò),現(xiàn)有代碼的確有可能存在錯(cuò)誤——但同時(shí)它也有可能正確無(wú)誤。由于相關(guān)應(yīng)用程序整體對(duì)于我們來(lái)說(shuō)存在價(jià)值(否則我們大可以直接將其拋棄),因此在不具備詳細(xì)信息的前提下,我們不妨假定其是正確的。“編寫測(cè)試會(huì)讓事情更麻煩的原因在于,這樣將給整體方案引入更多代碼”,這種態(tài)度既簡(jiǎn)單粗暴又站不住腳。
其次,請(qǐng)通過(guò)各種手段擴(kuò)大現(xiàn)有重構(gòu)、測(cè)試以及改進(jìn)成果的適用范圍,從而在實(shí)現(xiàn)***價(jià)值的同時(shí)盡可能縮減需要投入的精力。利用值進(jìn)行格式設(shè)定的GUI方案不該被優(yōu)先考慮。不過(guò)僅僅因?yàn)槟承﹥?nèi)容“非常簡(jiǎn)單”就放棄對(duì)其進(jìn)行測(cè)試也是不科學(xué)的。幾乎所有嚴(yán)重錯(cuò)誤的產(chǎn)生都是因?yàn)槿藗冏砸詾榱私庹鎸?shí)信息,但實(shí)際情況卻恰恰相反。
第三點(diǎn),“我們將在未來(lái)一次性進(jìn)行大規(guī)模重構(gòu)”,這個(gè)點(diǎn)子相當(dāng)不錯(cuò)。通常來(lái)講,就算暫時(shí)按兵不動(dòng)、將來(lái)也總有機(jī)會(huì)對(duì)遺留代碼進(jìn)行這樣一輪大規(guī)模清洗的。但我個(gè)人更信奉“穩(wěn)扎穩(wěn)打才能贏”的理念。
建立堅(jiān)實(shí)的基礎(chǔ)
網(wǎng)友guillaume31的回答(獲得34票支持):
我來(lái)談?wù)勛约旱膸c(diǎn)思考:
當(dāng)我們對(duì)遺留代碼進(jìn)行重構(gòu)時(shí),自己編寫的測(cè)試是否會(huì)偶爾與理想規(guī)范產(chǎn)生沖突其實(shí)并不重要。真正重要的是,它們能夠切實(shí)對(duì)程序的當(dāng)前運(yùn)行狀態(tài)作出評(píng)估。重構(gòu)的意義在于通過(guò)一系列細(xì)微且獨(dú)立的功能性步驟讓代碼變得更加簡(jiǎn)潔;大家肯定不希望在進(jìn)行重構(gòu)的同時(shí)還要操心漏洞修復(fù)工作。而且如果我們?cè)谶^(guò)程中發(fā)現(xiàn)了嚴(yán)重漏洞,它也不會(huì)隨著時(shí)間推移而自行消失。我們可以為其編寫一套回歸測(cè)試并暫時(shí)將其禁用,或者在自己的待辦事宜列表中加中這項(xiàng)漏洞修復(fù)任務(wù)以待日后處理。一次做一件事,這才叫有條不紊。
我也同意,純粹的GUI代碼很難進(jìn)行測(cè)試,可能也不太符合《高效處理遺留代碼》一書中的重構(gòu)指導(dǎo)方針。但這并不意味著我們應(yīng)該把與GUI層無(wú)關(guān)的其它代碼同樣拋在一邊,而不對(duì)其進(jìn)行任何測(cè)試。另外需要強(qiáng)調(diào)一點(diǎn),“12 lines, 2-3 if/else block”絕對(duì)不能算是無(wú)足輕重。任何哪怕涉及一丁點(diǎn)狀態(tài)邏輯的代碼都應(yīng)該被加以測(cè)試。
根據(jù)我的個(gè)人經(jīng)驗(yàn),大規(guī)模重構(gòu)工作難度很高而且效果一般都不好。如果大家不能為自己設(shè)定精確而且細(xì)化的執(zhí)行目標(biāo),那么整個(gè)重構(gòu)過(guò)程很可能陷入無(wú)休無(wú)止的惡性循環(huán),甚至最終變成像拉住頭發(fā)把自己提離地面這種根本沒(méi)有希望的任務(wù)。修改的程度越高,破壞原有內(nèi)容的風(fēng)險(xiǎn)就越大、導(dǎo)致我們陷入失敗境地的可能性也將愈發(fā)高企。
利用臨時(shí)性的小規(guī)模重構(gòu)逐步取得進(jìn)展并不意味著要“破壞未來(lái)大規(guī)模重構(gòu)的可能性”,這其實(shí)是一種促進(jìn)手段——即鞏固應(yīng)用程序所依賴的主干與根基。總之,我們應(yīng)當(dāng)采取這種步步為營(yíng)的應(yīng)對(duì)策略。
得不償失
網(wǎng)友Robbie Dee的回答(獲得9票支持):
某些企業(yè)的固有文化主張?jiān)试S開發(fā)人員在任何時(shí)間對(duì)代碼進(jìn)行強(qiáng)化,而非為其提供更為直接的附加價(jià)值——也就是新功能。
也許隨意發(fā)表個(gè)人意見有點(diǎn)多此一舉,但在我看來(lái)這種作法真的有點(diǎn)得不償失。純粹、簡(jiǎn)潔的代碼當(dāng)然能為開發(fā)人員帶來(lái)回報(bào),不過(guò)這樣的回報(bào)實(shí)在不夠明顯。
我個(gè)人贊同所謂“童子軍原則”(即加入童子軍的小朋友需要在‘離開營(yíng)地前進(jìn)行清掃活動(dòng)’),但其他人(正如大家所見)對(duì)此往往不以為然。
也就是說(shuō),不可知性與技術(shù)工作壓力會(huì)對(duì)軟件帶來(lái)不利影響。也許前任開發(fā)者時(shí)間太過(guò)緊張(或者可能是太懶甚至缺乏經(jīng)驗(yàn)),因此他們也許已經(jīng)在有限的時(shí)間內(nèi)拿出了雖然存在漏洞但已經(jīng)是***的設(shè)計(jì)方案。雖然對(duì)這些開發(fā)成果進(jìn)行重構(gòu)似乎能夠收到很好的效果,但同時(shí)也可能給原本正常運(yùn)作的代碼帶來(lái)新的漏洞、甚至進(jìn)而給用戶造成影響。
比較之下,某些改動(dòng)的風(fēng)險(xiǎn)可能比其它變更更低一些。舉例來(lái)說(shuō),我所面對(duì)的方案當(dāng)中存在大量重復(fù)代碼,完全可以被安全地從對(duì)業(yè)務(wù)影響很小的子程序中轉(zhuǎn)移出來(lái)。
總結(jié)來(lái)講,我們必須要在這樣的兩難選擇中作出判斷:既要考慮自己未來(lái)何時(shí)真正著手進(jìn)行重構(gòu),又要確定從零開始編寫自動(dòng)化測(cè)試到底能為業(yè)務(wù)帶來(lái)哪些切實(shí)存在的價(jià)值。
譯者:核子可樂(lè)
英文:Shoud I Write Tests for Legacy Code if There’s No Time for Refactoring