編譯:伯樂在線/maifans
這些軟件開發(fā)和測試的好實(shí)踐,可以幫你節(jié)省時(shí)間和避免問題。
加入一個(gè)企業(yè)文化和編程實(shí)踐已經(jīng)定型的新公司,可能會(huì)是一種令人沮喪的經(jīng)歷。當(dāng)我加入 Ansible 團(tuán)隊(duì)后,我決定整理我多年以來所學(xué)并為之奮斗的軟件工程實(shí)踐和準(zhǔn)則。這是一個(gè)不明確的也不夠詳盡的準(zhǔn)則列表,使用它們時(shí)需要智慧和靈活性。
我對測試充滿熱情,因?yàn)槲蚁嘈帕己玫臏y試實(shí)踐既能確保滿足最低質(zhì)量標(biāo)準(zhǔn)(可悲的是許多軟件產(chǎn)品做不到),并能指導(dǎo)和塑造開發(fā)本身。本文提到的這些準(zhǔn)則,很多是與測試實(shí)踐和理念相關(guān)的。其中一些準(zhǔn)則針對 Python 的,但大多數(shù)不是。(對于 Python 開發(fā)者,PEP 8 應(yīng)該是編程風(fēng)格和指南的首先。)
開發(fā)和測試的好實(shí)踐
1. YAGNI 原則:“You Aint Gonna Need It”。不要寫你認(rèn)為將來可能需要但現(xiàn)在不需要的代碼。這是為假想的未來用例編碼,這些代碼將不可避免地變成死代碼,或需要重寫,因?yàn)槲磥斫Y(jié)果總是與想象的稍有不同。
如果你寫代碼用于將來的用例,我將在代碼評審中對其質(zhì)疑。(你可能而且必須設(shè)計(jì) API,并確保未來的用例可用,但這是不同的問題。)
這條原則也適用于被注釋掉的代碼;如果一個(gè)被注釋掉代碼塊即將進(jìn)入一個(gè)發(fā)布版本,那么它就不應(yīng)該存在。如果代碼可能要還原,請為代碼刪除創(chuàng)建一個(gè)問題單并引用提交對象的哈希字符串。YAGNI 原則是敏捷編程的核心要素,這個(gè)話題最好的參考書是 Kent Beck 寫的《解析極限編程》(《Extreme Programming Explained》)。
2 . 測試不需要測試。用于測試需要的基礎(chǔ)設(shè)施、框架和庫需要測試。除非你真的需要不要測試瀏覽器或外部庫。測試你寫的代碼,而不是別人的代碼。
3.當(dāng)?shù)谌尉帉懴嗤拇a時(shí),也是將其提取成通用的輔助函數(shù)(并為其編寫測試)的正確時(shí)機(jī)。測試中的輔助函數(shù)不需要測試;但當(dāng)你將它們剔除出去然后再重用它們時(shí),它們需要測試。到你第三次編寫類似代碼的時(shí)候,你通常會(huì)清楚地認(rèn)識(shí)到你正在解決的通用問題的模型是什么。
4. 現(xiàn)在來談?wù)?API 設(shè)計(jì)(面向外部的對象API):把簡單的事情做簡單了,復(fù)雜的事情自然成為可能。首先設(shè)計(jì)簡單的用例,如果有可能最好是零配置或參數(shù)化。為更復(fù)雜和靈活的用例(如需要)增加選項(xiàng)或額外的 API 方法。
5. 快速失敗。檢查輸入,如果遇到無意義輸入或非法狀態(tài)則盡早失敗,最好是通過異?;蝈e(cuò)誤響應(yīng)使問題對調(diào)用者變得清晰。允許你的代碼處理“有創(chuàng)意”的用例(例如,除非真的需要,否則在做輸入驗(yàn)證的時(shí)候不要做類型檢查)。
6.單元測試測的是行為單元,而不是實(shí)現(xiàn)單元。我們的目標(biāo)是在改動(dòng)實(shí)現(xiàn)的情況下不改動(dòng)行為,也不必更新測試,盡管這個(gè)目標(biāo)不總是能實(shí)現(xiàn)。因此在可能的情況下將測試對象視為黑盒,通過公共 API 測試,而不調(diào)用私有方法或玩弄狀態(tài)位。
在一些復(fù)雜的情況可能做不到,如在特定的復(fù)雜狀態(tài)下測試行為,以找到一個(gè)偶發(fā)的錯(cuò)誤。這一點(diǎn)對于寫測試非常有幫助,因?yàn)樗仁鼓阍趯憸y試代碼之前思考你代碼的行為以及你將如何測試它。測試首先鼓勵(lì)更小,更模塊化的代碼單元,這通常意味著好代碼。關(guān)于“測試優(yōu)先”方法有一本很好的入門參考書,就是 Kent Beck 寫的《測試驅(qū)動(dòng)開發(fā)》(《Test Driven Development by Example》)
7. 對于單元測試(包括測試基礎(chǔ)設(shè)施測試),所有代碼路徑都應(yīng)該被測到。100% 覆蓋是一個(gè)好的開始。你不能覆蓋所有可能狀態(tài)的排列/組合(組合性爆炸),因此這個(gè)問題需要考慮。只有當(dāng)有很好理由的情況下才允許有代碼路徑未經(jīng)測試。沒有時(shí)間不是一個(gè)好理由,最終會(huì)花費(fèi)更多的時(shí)間??赡艿暮美碛砂ǎ赫嬲牟豢蓽y(以任何有意義的方式),現(xiàn)實(shí)中不可能發(fā)生,或被其它測試覆蓋。沒有測試的代碼是一種債。測量覆蓋率和拒絕減少覆蓋率 PR(拉取請求) 是確保你在正確的方向演進(jìn)的一種方式。
8. 代碼是敵人:它可能出錯(cuò),并且需要維護(hù)。少寫代碼,刪除代碼,不要寫你不需要的代碼。
9. 隨著時(shí)間的推移,代碼注釋不可避免地成為謊言。在現(xiàn)實(shí)中,很少有人在事情變化的時(shí)候更新注釋。通過良好的命名法和已知的編程風(fēng)格,努力使你的代碼可讀和自文檔化。
對于那些晦澀的代碼,一定要寫注釋,例如偶發(fā)錯(cuò)誤或意外情況的變通方案,或者必要的優(yōu)化。解釋代碼的意圖和及其原因,而不是解釋代碼在做什么。(順便說一句,有些觀點(diǎn)認(rèn)為注釋變謊言是有爭議的。我仍然認(rèn)為這是正確的,《程序設(shè)計(jì)實(shí)踐》(《The Practice of Programming》)的作者 Kernighan 和 Pike 同意我的觀點(diǎn)。)
10. 防守思維??偸强紤]什么會(huì)出錯(cuò),無效的輸入會(huì)引發(fā)什么,什么可能會(huì)失敗,這將有助于你在許多錯(cuò)誤發(fā)生之前發(fā)現(xiàn)他們。
11.無狀態(tài)和無副作用的單元測試,其邏輯應(yīng)簡單。將邏輯分解成單獨(dú)的函數(shù),而不是將邏輯混合到有狀態(tài)和充滿副作用代碼中。將有狀態(tài)代碼和有副作用代碼,分為較小的更容易模擬的函數(shù)和無副作用地單元測試。(測試的開銷越小意味著更快的測試)副作用確實(shí)需要測試,但是測試一次然后處處模擬它們通常是一個(gè)好的模式。
12. 全局變量不好。函數(shù)優(yōu)于類型。對象可能比復(fù)雜的數(shù)據(jù)結(jié)構(gòu)更好。
13.使用 Python 內(nèi)置類型及其方法將比自己編寫的類型運(yùn)行快(除非你用C語言編寫)。如果性能是一個(gè)考慮因素,請嘗試弄懂如何使用標(biāo)準(zhǔn)的內(nèi)置類型,而不是自定義對象。
14. 依賴注入是一個(gè)實(shí)用的編程模式,明確你的依賴是什么和它們來自哪里。(對象,方法等以參數(shù)的形式接收它們的依賴,而不是實(shí)例化新對象本身。)這確實(shí)讓 API 簽名更復(fù)雜,所以這里需要權(quán)衡。如果一個(gè)方法最后為所有的依賴設(shè)置了10個(gè)參數(shù),那這是一個(gè)不錯(cuò)的信號(hào),不管為什么你的代碼做得太多。關(guān)于依賴注入的權(quán)威文章是 Martin Fowler 的《控制反轉(zhuǎn)容器&依賴注入模式》(《Inversion of Control Containers and the Dependency Injection Pattern》)。
15. 需要模擬測試的代碼越多,你的代碼就越糟糕。為了測試一個(gè)特定的行為,需要實(shí)例化和牽扯的代碼越多,代碼越糟糕。我們的目標(biāo)是小型可測試的單元,以及更高級(jí)別的集成和功能測試,以測試各單元是否配合正確。
16. 面向外部的 API 是“預(yù)先設(shè)計(jì)”——同時(shí)要考慮未來的用例——真正重要的地方。改變 API 對我們和用戶來說是一種痛苦,造成向后不兼容是可怕的(盡管有時(shí)無法避免的)。設(shè)計(jì)面向外部的 API 要細(xì)心,仍然要堅(jiān)持“把簡單的事情做簡單”的原則。
17.如果函數(shù)或方法超過 30 行代碼,考慮分解它。一個(gè)良好的模塊大約 500 行。測試文件往往比這個(gè)要大。
18 .不要在對象構(gòu)造函數(shù)中工作,這里很難測試而且經(jīng)常發(fā)生意外。不要在__init__ .py中添加代碼(導(dǎo)入命名空間除外)。__init__ .py不是程序員通常期望找代碼的地方,所以它是個(gè)“驚喜”。
19. DRY(不要重復(fù)自己)在測試中沒有在生產(chǎn)代碼中要緊。單個(gè)測試文件的可讀性比可維護(hù)性更重要(跳出模塊復(fù)用的限制)。這是因?yàn)闇y試是單獨(dú)被執(zhí)行和閱讀的,而且它們自己不是大系統(tǒng)的一部分。雖然在很多重復(fù)的時(shí)候創(chuàng)建可重用的組件更方便,但相較于產(chǎn)品代碼,測試代碼較少考慮。
20.每當(dāng)你看到有需要有機(jī)會(huì)就重構(gòu)。編程是關(guān)于抽象的,你的抽象映射越接近問題域,代碼就越容易理解和維護(hù)。隨著系統(tǒng)的有機(jī)增長,需要改變結(jié)構(gòu)以擴(kuò)大它們的用例。系統(tǒng)越來越多的抽象和結(jié)構(gòu),如果不改變它們就成為技術(shù)性的債務(wù)。它會(huì)是更加痛苦的工作(更慢,越來越多的錯(cuò)誤)。在特性開發(fā)估計(jì)中請考慮清除技術(shù)債務(wù)(重構(gòu))的成本。你遺留債務(wù)的時(shí)間越長,積累的利息就越高。關(guān)于重構(gòu)和和測試的一本很棒的書是 Michael Feathers 的《修改代碼的藝術(shù)》(《Working Effectively with Legacy Code》)。
21. 代碼正確為第一位,速度快第二位。在處理性能問題時(shí),在修復(fù)錯(cuò)誤之前先要做性能剖析。通常瓶頸不是你認(rèn)為的那樣。如果寫晦澀的代碼的唯一價(jià)值就是更快而且你已經(jīng)做過性能剖析并證明了,那么它實(shí)際就是值得的。編寫測試定期檢測你要做性能剖析的代碼,這樣可以很容易讓你知道你什么時(shí)候測試過。測試可以留在測試套件中,以防止性能退化。(通常情況下,添加定時(shí)代碼總會(huì)改變代碼的性能特性,使性能工作成為令人沮喪的任務(wù)之一。)
22. 當(dāng)更小、范圍有限的單元測試失敗的時(shí)候,可以給出更多有價(jià)值的信息,告訴你具體是什么錯(cuò)誤。如果一個(gè)測試牽涉了半個(gè)系統(tǒng)來測試行為,那么它需要更多的調(diào)查以確定什么是錯(cuò)誤的。一般來說,運(yùn)行超過 0.1 秒的測試不是單元測試。沒有所謂的慢單元測試。用限定范圍的單元測試測試行為,你的測試行為扮演了事實(shí)上的代碼規(guī)范。理想情況下如果有人想了解你的代碼,他們應(yīng)該能夠把測試套件轉(zhuǎn)換為行為的“文檔”。Gary Bernhardt 的《快測,慢測》(《Fast Test, Slow Test》)是關(guān)于單元測試實(shí)踐的一篇很棒的演講。
23. ”非我所創(chuàng)“不像人們說的那么壞。如果代碼是我們寫的,那么我們知道它是什么,我們知道如何維護(hù)它,在我們可以在適當(dāng)?shù)臅r(shí)候自由地?cái)U(kuò)展和修改它。這遵循了 YAGNI 原則:我們用那些適合我們需要用例的特定代碼,而不用我們不需要的可以做復(fù)雜事情的通用代碼。另一方面,代碼是敵人,擁有必要的代碼比擁有更多的代碼更好。引入新的依賴關(guān)系時(shí)要權(quán)衡。
24. 共享代碼所有權(quán)是我們目標(biāo);沉默的知識(shí)不是好知識(shí)。這意味著最低限度要討論或記錄設(shè)計(jì)決策和重要的實(shí)施決策。代碼評審(Code Review)是開始討論設(shè)計(jì)決策的最壞時(shí)刻,因?yàn)樵诖a編寫后,很難徹底更改。(當(dāng)然在評審時(shí)指出并修改設(shè)計(jì)錯(cuò)誤比沒有好。)
25. 生成器很棒!它們通常比迭代或重復(fù)執(zhí)行的狀態(tài)對象更短更容易理解。David Beazley的《系統(tǒng)程序員的生成器訣竅》(《Generator Tricks for Systems Programmers》)是關(guān)于生成器一個(gè)很好的介紹。
26.讓我們成為工程師!讓我們考慮設(shè)計(jì)、構(gòu)建健壯并實(shí)現(xiàn)良好的系統(tǒng),而不是做膨脹的有機(jī)怪物。然而編程是一種平衡。我們并不總是建造火箭。過度設(shè)計(jì)(洋蔥架構(gòu))同設(shè)計(jì)不完善的代碼一樣,處理起來非常痛苦。Robert Martin 的作品幾乎都值得一讀,《架構(gòu)之潔:一個(gè)工匠的軟件結(jié)構(gòu)和設(shè)計(jì)指南》(《Clean Architecture: A Craftsman’s Guide to Software Structure and Design》)是這個(gè)話題一個(gè)很好的資源?!对O(shè)計(jì)模式》(《Design Patterns》)是每一位工程師都應(yīng)該閱讀的經(jīng)典編程書。
27. 間歇失敗的測試會(huì)侵蝕測試套件的價(jià)值,以至于最終每個(gè)人都忽略測試運(yùn)行結(jié)果,因?yàn)榭傆幸恍┦〉氖虑?。修?fù)或刪除間歇性失敗測試是痛苦的,但這些努力是值得的。
28. 一般來說,特別是在測試中,在需要等待一個(gè)特定的變化時(shí)候不要采用休眠隨機(jī)時(shí)間的方式。Voodoo(Python 庫) 的 sleeps 很難理解而且使你的測試套件變慢。
29. 至少讓你的測試失敗一次。故意加入一個(gè)錯(cuò)誤,并確保它失敗,或在測試的行為不完整的情況下運(yùn)行測試。否則你不知道你真的在測試什么。瞎寫的測試實(shí)際上不能測試任何東西或它很可能永遠(yuǎn)不會(huì)失敗。
30. 最后一點(diǎn):只關(guān)注特性改進(jìn)是開發(fā)軟件的一種可怕方式。如果開發(fā)者的工作不能確保最好的結(jié)果,那么不要讓他們?yōu)樽约旱墓ぷ鞲械阶院?。不處理技術(shù)債務(wù)會(huì)使開發(fā)變慢并最終導(dǎo)致產(chǎn)品更糟,問題更多。
感謝 Ansible 團(tuán)隊(duì),尤其是 Wayne Witzel,為改善這個(gè)列表中的準(zhǔn)則而提出的意見和建議。
文章名稱:軟件開發(fā)和測試的 30 個(gè)最佳實(shí)踐
標(biāo)題URL:http://muchs.cn/news/116894.html
網(wǎng)站建設(shè)、網(wǎng)絡(luò)推廣公司-創(chuàng)新互聯(lián),是專注品牌與效果的網(wǎng)站制作,網(wǎng)絡(luò)營銷seo公司;服務(wù)項(xiàng)目有軟件開發(fā)等
廣告
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源:
創(chuàng)新互聯(lián)