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