軟件工程規(guī)則和測(cè)試積累的最佳實(shí)踐阴颖,能幫助企業(yè)顯著節(jié)省時(shí)間活喊,減少痛點(diǎn)。良好的測(cè)試實(shí)踐既可以確保質(zhì)量標(biāo)準(zhǔn)量愧,也可以指導(dǎo)和塑造每個(gè)程序員的發(fā)展钾菊。
小數(shù)今天推送的這篇文章帅矗,文中的許多原則都與測(cè)試實(shí)踐和理想有關(guān),有一些還是Python特有的煞烫。正如本文作者所說(shuō)浑此,程序員的那些固執(zhí)己見(jiàn),強(qiáng)烈的意見(jiàn)表達(dá)和堅(jiān)持滞详,往往是激情的最好表現(xiàn)凛俱。
- YAGNI:不要編寫(xiě)現(xiàn)在尚不需要,而將來(lái)可能需要的代碼×霞ⅲ現(xiàn)在的代碼不可避免地會(huì)變成死代碼或需要重寫(xiě)蒲犬,因?yàn)槲磥?lái)的用例總是與設(shè)想的不同。如果把代碼放在未來(lái)的用例中岸啡,在代碼審查中要提出質(zhì)疑暖哨。(例如,可以并且必須設(shè)計(jì)API來(lái)允許將來(lái)的用例凰狞,不過(guò),這是另外一個(gè)問(wèn)題沛慢。)
注解代碼也是如此赡若。如果一個(gè)注釋代碼塊進(jìn)入發(fā)行版,它就不應(yīng)該存在团甲。如果是可以恢復(fù)的代碼逾冬,創(chuàng)建一個(gè)ticket,并引用代碼刪除的提交散列躺苦。YAGNI 是敏捷編程的核心元素身腻。Kent Beck提供的極限編程解釋(Extreme Programming Explained)就是最好的參考。
2.測(cè)試本身不需要測(cè)試匹厘。用于測(cè)試的基礎(chǔ)架構(gòu)嘀趟,框架和庫(kù)需要測(cè)試。除非確實(shí)需要愈诚,通常不要測(cè)試瀏覽器或外部庫(kù)她按。測(cè)試你寫(xiě)的代碼,而并非其他人的炕柔。
3.第三次編寫(xiě)相同代碼時(shí)酌泰,是將其抽取到通用幫助器(并為其編寫(xiě)測(cè)試)的正確時(shí)機(jī)。測(cè)試中的輔助功能不需要測(cè)試; 當(dāng)把它們分解出來(lái)并進(jìn)行重用時(shí)匕累,才需要測(cè)試陵刹。到第三次編寫(xiě)類似的代碼時(shí),會(huì)清楚地知道正在解決什么樣的問(wèn)題欢嘿。
4.涉及到API設(shè)計(jì):簡(jiǎn)單的事情簡(jiǎn)單化; 復(fù)雜的事情可能如果可能也簡(jiǎn)單化衰琐。首先設(shè)計(jì)一個(gè)簡(jiǎn)單的情況也糊,如果可能,最好使用零配置或參數(shù)化碘耳。為更復(fù)雜和更靈活的用例添加選項(xiàng)或其他API方法显设。
5.快速失敗。盡可能早地檢查輸入辛辨,并在無(wú)意義的輸入或無(wú)效狀態(tài)上失敗捕捂,使用異常或錯(cuò)誤響應(yīng)斗搞,來(lái)確定問(wèn)題指攒。允許代碼“創(chuàng)新”,也就是說(shuō)僻焚,通常情況下允悦,不要對(duì)輸入驗(yàn)證進(jìn)行類型檢查。
- 單元測(cè)試是對(duì)行為單元的測(cè)試虑啤,而不是對(duì)實(shí)現(xiàn)單元隙弛。改變實(shí)現(xiàn),而不是改變行為或被迫改變?nèi)魏螠y(cè)試狞山,當(dāng)然這點(diǎn)并不是總能實(shí)現(xiàn)全闷。在可能的情況下,將測(cè)試對(duì)象視為黑盒子萍启,通過(guò)公共 API 進(jìn)行測(cè)試总珠,而無(wú)需調(diào)用私有方法或修改狀態(tài)。
對(duì)于一些復(fù)雜的場(chǎng)景勘纯,比如一些特定復(fù)雜狀態(tài)下局服,測(cè)試行為找到一個(gè)難以理解的錯(cuò)誤。編寫(xiě)測(cè)試確實(shí)有助于解決這個(gè)問(wèn)題驳遵,因?yàn)樗鼤?huì)迫使你在編寫(xiě)代碼之前淫奔,考慮清楚代碼的行為以及如何測(cè)試。測(cè)試首先鼓勵(lì)更小超埋,更模塊化的代碼單元搏讶。
7.對(duì)于單元測(cè)試(包括測(cè)試基礎(chǔ)結(jié)構(gòu)測(cè)試),應(yīng)該測(cè)試所有的代碼路徑霍殴。100%的覆蓋率是一個(gè)很好的起點(diǎn)媒惕。你不能涵蓋所有可能的排列/組合狀態(tài)。在有很好的理由時(shí)来庭,代碼路徑才能被測(cè)試妒蔚。缺少時(shí)間不是一個(gè)好的理由,這往往導(dǎo)致最終花費(fèi)更多時(shí)間。好的理由包括:真正無(wú)法測(cè)試肴盏,在實(shí)踐中不可能實(shí)現(xiàn)科盛,或者在測(cè)試中其他地方被覆蓋。沒(méi)有測(cè)試的代碼要承擔(dān)責(zé)任菜皂。衡量覆蓋率贞绵,并拒絕降低覆蓋率的百分比是朝正確方向前進(jìn)的一種方法。
8.代碼是敵人:可能出錯(cuò)恍飘,需要維護(hù)榨崩。少寫(xiě)代碼。刪除代碼章母。不要編寫(xiě)你不需要的代碼母蛛。
9.隨著時(shí)間的推移,代碼注釋不可避免地成為謊言乳怎。實(shí)際上彩郊,很少有人在事情發(fā)生變化時(shí)更新評(píng)論。通過(guò)良好的命名實(shí)踐和已知的編程風(fēng)格蚪缀,努力使代碼有可讀性和自我記錄秫逝。
不明確的代碼確實(shí)需要注釋,比如圍繞一個(gè)模糊的錯(cuò)誤或不可能的條件询枚,或者必要的優(yōu)化筷登。
10.防守地寫(xiě)。要總是想可能出現(xiàn)什么問(wèn)題哩盲,無(wú)效輸入會(huì)發(fā)生什么,什么可能導(dǎo)致失敗狈醉,這有助于在錯(cuò)誤發(fā)生前發(fā)現(xiàn)錯(cuò)誤廉油。
11.如果邏輯無(wú)狀態(tài)且無(wú)副作用,則易于進(jìn)行單元測(cè)試苗傅。將邏輯分解成獨(dú)立的函數(shù)抒线,而不是混合成有狀態(tài)和副作用的代碼。將有狀態(tài)和帶副作用的代碼分離成更小的函數(shù)渣慕,可以更容易地模擬和單元測(cè)試嘶炭。副作用確實(shí)需要測(cè)試,測(cè)試一次并在其他地方進(jìn)行模擬逊桦,是比較好的做法眨猎。
12.Globals are bad。功能優(yōu)于類型强经,對(duì)象比復(fù)雜的數(shù)據(jù)結(jié)構(gòu)更好睡陪。
13.使用 Python 內(nèi)置類型和方法比編寫(xiě)自己的類型更快(除非用C編寫(xiě))。如果考慮性能,嘗試解決如何使用標(biāo)準(zhǔn)內(nèi)置類型而不是自定義對(duì)象兰迫。
依賴注入是一種有用的編程模式信殊,可以清楚了解依賴關(guān)系。(讓對(duì)象汁果,方法等接收依賴關(guān)系作為參數(shù)涡拘,而不是自己實(shí)例化新的對(duì)象。)這是一種交換据德,使API簽名更加復(fù)雜鳄乏。如果完成依賴項(xiàng)需要使用10個(gè)參數(shù),說(shuō)明代碼太多了晋控。
越需要模擬測(cè)試代碼汞窗,它就會(huì)越糟糕。需要實(shí)例化和放置的代碼越多赡译,用來(lái)測(cè)試特定的行為仲吏,代碼就越糟糕。測(cè)試的目標(biāo)是小型可測(cè)試單元蝌焚,和更高級(jí)別的集成和功能測(cè)試裹唆,來(lái)測(cè)試這些單元是否正確地協(xié)作。
16.面向外部的 API 是要“預(yù)先設(shè)計(jì)”的只洒,它關(guān)系到未來(lái)用例许帐。改變 API 對(duì)自己和用戶來(lái)說(shuō)都是一種痛苦,而創(chuàng)造向后兼容性是非潮锨矗可怕的成畦,盡管有時(shí)不可避免。精心設(shè)計(jì)面向外部的API涝开,仍然堅(jiān)持“簡(jiǎn)單的事情簡(jiǎn)單化”的原則循帐。
17.如果一個(gè)函數(shù)或方法超過(guò)30行代碼,就要考慮分解舀武。好的最大模塊大小約為500線拄养。測(cè)試文件往往比這個(gè)更長(zhǎng)。
18.不要在難以測(cè)試的對(duì)象構(gòu)造函數(shù)中工作银舱。不要把代碼放在init.py中瘪匿。程序員不希望在init.py找到代碼。
- DRY(Don’t Repeat Yourself不要重復(fù)自己)在測(cè)試中比在生產(chǎn)中重要得多寻馏。單個(gè)測(cè)試文件的可讀性比可維護(hù)性更重要(突破可重用的塊)棋弥。這是因?yàn)闇y(cè)試是單獨(dú)執(zhí)行和讀取的,而不是作為大系統(tǒng)的一部分诚欠。過(guò)多的重復(fù)意味著可重用的組件可以方便地創(chuàng)建嘁锯,但是與生產(chǎn)相比宪祥,測(cè)試的重要性低得多。
20.當(dāng)看到需要并有機(jī)會(huì)時(shí)進(jìn)行重構(gòu)家乘。編程是關(guān)于抽象的蝗羊,抽象映射越接近問(wèn)題域,代碼就越容易理解和維護(hù)仁锯。隨著系統(tǒng)有機(jī)地增長(zhǎng)耀找,需要為擴(kuò)展用例而改變結(jié)構(gòu)。系統(tǒng)超出抽象和結(jié)構(gòu)业崖,而不改變它們成為技術(shù)債務(wù)野芒,這會(huì)更痛苦,更緩慢双炕,更多bug狞悲。包括重構(gòu)成本,包括對(duì)功能的預(yù)估妇斤。你把債務(wù)拖得越久摇锋,它積累的利息就越高。Michael Feathers 撰寫(xiě)了一本關(guān)于重構(gòu)和測(cè)試的書(shū)籍站超,其中著重介紹了遺留代碼荸恕。
21.正確的編寫(xiě)代碼第一,速度第二死相。處理性能問(wèn)題時(shí)融求,請(qǐng)務(wù)必在修復(fù)之前進(jìn)行配置。瓶頸通常出乎你的意料算撮。編寫(xiě)難懂的代碼如果是為了速度更快生宛,為此你進(jìn)行了配置并證明這么做是值得的,那么也僅僅是速度上值得而已肮柜。編寫(xiě)一個(gè)測(cè)試茅糜,訓(xùn)練正在配置的代碼,讓它知道什么情況下會(huì)變得更簡(jiǎn)單素挽,并且留在測(cè)試套件中,來(lái)防止性能下降狸驳。(通常情況下预明,添加計(jì)時(shí)代碼總是會(huì)改變代碼的性能特點(diǎn),使性能變得經(jīng)常差強(qiáng)人意耙箍。)
22.更小范圍的單元測(cè)試在失敗時(shí)會(huì)提供更有價(jià)值的信息撰糠。要判定錯(cuò)誤,有一半的系統(tǒng)測(cè)試行為的測(cè)試需要依據(jù)更多的調(diào)查辩昆。一般來(lái)說(shuō)阅酪,運(yùn)行時(shí)間超過(guò)0.1秒的測(cè)試不是單元測(cè)試。沒(méi)有所謂的慢單元測(cè)試。通過(guò)嚴(yán)格范圍的單元測(cè)試測(cè)試行為术辐,你的測(cè)試可以作為代碼實(shí)際規(guī)范砚尽。理想情況下,如果有人想了解你的代碼辉词,應(yīng)該能夠?qū)y(cè)試套件作為行為的“文檔”必孤。
- “這里沒(méi)有發(fā)明”并不像人們所說(shuō)的那么糟糕。如果我們編寫(xiě)代碼瑞躺,我們知道它的作用敷搪,知道如何維護(hù)它,可以自由地?cái)U(kuò)展和修改它幢哨。這是遵循了YAGNI的原則:我們有需要用例的特定代碼赡勘,而并非通用代碼,通用代碼會(huì)給我們不需要的部分帶來(lái)復(fù)雜性捞镰。另一方面闸与,代碼是敵人,擁有更多的代碼沒(méi)有好處曼振,當(dāng)引入新的依賴關(guān)系時(shí)需要考慮權(quán)衡几迄。
24.共享代碼所有權(quán)是目標(biāo)。孤立的知識(shí)沒(méi)什么好冰评,這意味著至少要討論映胁、記錄貫徹設(shè)計(jì)決策和實(shí)施決策。代碼審查時(shí)開(kāi)始討論設(shè)計(jì)決策是最糟糕甲雅,因?yàn)樵诰帉?xiě)代碼之后進(jìn)行徹底的更改太難克服了解孙。當(dāng)然,在Review時(shí)指出和修改設(shè)計(jì)錯(cuò)誤抛人,比永遠(yuǎn)不總結(jié)不思考要好得多弛姜。
- Generators rock! 與有狀態(tài)的對(duì)象相比,它們用于迭代或重復(fù)執(zhí)行通常更短妖枚,更容易理解廷臼。
26.讓我們成為工程師!設(shè)計(jì)绝页、建立強(qiáng)大和良好實(shí)施的系統(tǒng)荠商,而不是增加有機(jī)怪物。編程是一個(gè)平衡的行為续誉,我們并不總是建造火箭飛船莱没,過(guò)度設(shè)計(jì)(onion architecture洋蔥架構(gòu))與設(shè)計(jì)不足的代碼同樣令人痛苦。羅伯特?馬丁(Robert Martin)所做的幾乎任何事情都值得一讀酷鸦,“ 干凈架構(gòu):軟件結(jié)構(gòu)和設(shè)計(jì)指南”( Clean Architecture: A Craftsman’s Guide to Software Structure and Design )是這方面的好資源饰躲。設(shè)計(jì)模式( Design Patterns)是每個(gè)工程師應(yīng)該閱讀的經(jīng)典編程書(shū)籍牙咏。
27.間歇性地失敗的測(cè)試會(huì)侵蝕測(cè)試套件的價(jià)值,以至于最終每個(gè)人都忽略測(cè)試運(yùn)行的結(jié)果嘹裂。修復(fù)或刪除間歇性失敗的測(cè)試是痛苦的妄壶,但值得努力。
28.一般來(lái)說(shuō)焦蘑,尤其是在測(cè)試中盯拱,等待一個(gè)具體的改變,而不是任意時(shí)間內(nèi)sleeping例嘱。Voodoo sleeps 很難理解狡逢,而且會(huì)放慢測(cè)試套件。
29.至少要看到你的測(cè)試失敗過(guò)一次拼卵。將一個(gè)蓄意的 bug 放入讓它失敗奢浑,或在測(cè)試行為完成之前運(yùn)行測(cè)試。否則腋腮,你不知道你真的在測(cè)試雀彼。偶爾寫(xiě)寫(xiě)測(cè)試代碼實(shí)際并不測(cè)試任何東西,或?qū)懹肋h(yuǎn)不會(huì)失敗的測(cè)試即寡,都是很容易的徊哑。
30.最后,管理要點(diǎn):持續(xù)的功能研發(fā)是開(kāi)發(fā)軟件的一個(gè)可怕方法聪富。不讓開(kāi)發(fā)人員在工作感到驕傲, 就不會(huì)從中獲得最大的利益莺丑。不解決技術(shù)債務(wù),會(huì)拖慢開(kāi)發(fā)速度墩蔓,并導(dǎo)致更糟糕的梢莽、更多bug的產(chǎn)品。
原文作者:Michael Foord
原文鏈接:
https://opensource.com/article/17/5/30-best-practices-software-development-and-testing