軟件開發和測試的 30 個最佳實踐[轉載自伯樂在線]

軟件開發和測試的 30 個最佳實踐

軟件開發和測試的 30 個最佳實踐

 2017-06-29 伯樂在線 伯樂在線

(點擊上方公衆號,可快速關注)


編譯:伯樂在線/maifans

如有好文章投稿,請點擊 → 這裏瞭解詳情


這些軟件開發和測試的最佳實踐,可以幫你節省時間和避免問題。

加入一個企業文化和編程實踐已經定型的新公司,可能會是一種令人沮喪的經歷。當我加入 Ansible 團隊後,我決定整理我多年以來所學併爲之奮鬥的軟件工程實踐和準則。這是一個不明確的也不夠詳盡的準則列表,使用它們時需要智慧和靈活性。

我對測試充滿熱情,因爲我相信良好的測試實踐既能確保滿足最低質量標準(可悲的是許多軟件產品做不到),並能指導和塑造開發本身。本文提到的這些準則,很多是與測試實踐和理念相關的。其中一些準則針對 Python 的,但大多數不是。(對於 Python 開發者,PEP 8 應該是編程風格和指南的首先。)


開發和測試的最佳實踐

1. YAGNI 原則:“You Aint Gonna Need It”。不要寫你認爲將來可能需要但現在不需要的代碼。這是爲假想的未來用例編碼,這些代碼將不可避免地變成死代碼,或需要重寫,因爲未來結果總是與想象的稍有不同。

如果你寫代碼用於將來的用例,我將在代碼評審中對其質疑。(你可能而且必須設計 API,並確保未來的用例可用,但這是不同的問題。)

這條原則也適用於被註釋掉的代碼;如果一個被註釋掉代碼塊即將進入一個發佈版本,那麼它就不應該存在。如果代碼可能要還原,請爲代碼刪除創建一個問題單並引用提交對象的哈希字符串。YAGNI 原則是敏捷編程的核心要素,這個話題最好的參考書是 Kent Beck 寫的《解析極限編程》(《Extreme Programming Explained》)。

2 . 測試不需要測試。用於測試需要的基礎設施、框架和庫需要測試。除非你真的需要不要測試瀏覽器或外部庫。測試你寫的代碼,而不是別人的代碼。

3.當第三次編寫相同的代碼時,也是將其提取成通用的輔助函數(併爲其編寫測試)的正確時機。測試中的輔助函數不需要測試;但當你將它們剔除出去然後再重用它們時,它們需要測試。到你第三次編寫類似代碼的時候,你通常會清楚地認識到你正在解決的通用問題的模型是什麼。

4. 現在來談談 API 設計(面向外部的對象API):把簡單的事情做簡單了,複雜的事情自然成爲可能。首先設計簡單的用例,如果有可能最好是零配置或參數化。爲更復雜和靈活的用例(如需要)增加選項或額外的 API 方法。

5. 快速失敗。檢查輸入,如果遇到無意義輸入或非法狀態則儘早失敗,最好是通過異常或錯誤響應使問題對調用者變得清晰。允許你的代碼處理“有創意”的用例(例如,除非真的需要,否則在做輸入驗證的時候不要做類型檢查)。

6.單元測試測的是行爲單元,而不是實現單元。我們的目標是在改動實現的情況下不改動行爲,也不必更新測試,儘管這個目標不總是能實現。因此在可能的情況下將測試對象視爲黑盒,通過公共 API 測試,而不調用私有方法或玩弄狀態位。

在一些複雜的情況可能做不到,如在特定的複雜狀態下測試行爲,以找到一個偶發的錯誤。這一點對於寫測試非常有幫助,因爲它迫使你在寫測試代碼之前思考你代碼的行爲以及你將如何測試它。測試首先鼓勵更小,更模塊化的代碼單元,這通常意味着好代碼。關於“測試優先”方法有一本很好的入門參考書,就是 Kent Beck 寫的《測試驅動開發》(《Test Driven Development by Example》)

7. 對於單元測試(包括測試基礎設施測試),所有代碼路徑都應該被測到。100% 覆蓋是一個好的開始。你不能覆蓋所有可能狀態的排列/組合(組合性爆炸),因此這個問題需要考慮。只有當有很好理由的情況下才允許有代碼路徑未經測試。沒有時間不是一個好理由,最終會花費更多的時間。可能的好理由包括:真正的不可測(以任何有意義的方式),現實中不可能發生,或被其它測試覆蓋。沒有測試的代碼是一種債。測量覆蓋率和拒絕減少覆蓋率 PR(拉取請求) 是確保你在正確的方向演進的一種方式。

8. 代碼是敵人:它可能出錯,並且需要維護。少寫代碼,刪除代碼,不要寫你不需要的代碼。

9. 隨着時間的推移,代碼註釋不可避免地成爲謊言。在現實中,很少有人在事情變化的時候更新註釋。通過良好的命名法和已知的編程風格,努力使你的代碼可讀和自文檔化。

對於那些晦澀的代碼,一定要寫註釋,例如偶發錯誤或意外情況的變通方案,或者必要的優化。解釋代碼的意圖和及其原因,而不是解釋代碼在做什麼。(順便說一句,有些觀點認爲註釋變謊言是有爭議的。我仍然認爲這是正確的,《程序設計實踐》(《The Practice of Programming》)的作者 Kernighan 和 Pike 同意我的觀點。)

10. 防守思維。總是考慮什麼會出錯,無效的輸入會引發什麼,什麼可能會失敗,這將有助於你在許多錯誤發生之前發現他們。

11.無狀態和無副作用的單元測試,其邏輯應簡單。將邏輯分解成單獨的函數,而不是將邏輯混合到有狀態和充滿副作用代碼中。將有狀態代碼和有副作用代碼,分爲較小的更容易模擬的函數和無副作用地單元測試。(測試的開銷越小意味着更快的測試)副作用確實需要測試,但是測試一次然後處處模擬它們通常是一個好的模式。

12. 全局變量不好。函數優於類型。對象可能比複雜的數據結構更好。

13.使用 Python 內置類型及其方法將比自己編寫的類型運行快(除非你用C語言編寫)。如果性能是一個考慮因素,請嘗試弄懂如何使用標準的內置類型,而不是自定義對象。

14. 依賴注入是一個實用的編程模式,明確你的依賴是什麼和它們來自哪裏。(對象,方法等以參數的形式接收它們的依賴,而不是實例化新對象本身。)這確實讓 API 簽名更復雜,所以這裏需要權衡。如果一個方法最後爲所有的依賴設置了10個參數,那這是一個不錯的信號,不管爲什麼你的代碼做得太多。關於依賴注入的權威文章是 Martin Fowler 的《控制反轉容器&依賴注入模式》(《Inversion of Control Containers and the Dependency Injection Pattern》)。

15. 需要模擬測試的代碼越多,你的代碼就越糟糕。爲了測試一個特定的行爲,需要實例化和牽扯的代碼越多,代碼越糟糕。我們的目標是小型可測試的單元,以及更高級別的集成和功能測試,以測試各單元是否配合正確。

16. 面向外部的 API 是“預先設計”——同時要考慮未來的用例——真正重要的地方。改變 API 對我們和用戶來說是一種痛苦,造成向後不兼容是可怕的(儘管有時無法避免的)。設計面向外部的 API 要細心,仍然要堅持“把簡單的事情做簡單”的原則。

17.如果函數或方法超過 30 行代碼,考慮分解它。一個良好的模塊最大約 500 行。測試文件往往比這個要大。

18 .不要在對象構造函數中工作,這裏很難測試而且經常發生意外。不要在__init__ .py中添加代碼(導入命名空間除外)。__init__ .py不是程序員通常期望找代碼的地方,所以它是個“驚喜”。

19. DRY(不要重複自己)在測試中沒有在生產代碼中要緊。單個測試文件的可讀性比可維護性更重要(跳出模塊複用的限制)。這是因爲測試是單獨被執行和閱讀的,而且它們自己不是大系統的一部分。雖然在很多重複的時候創建可重用的組件更方便,但相較於產品代碼,測試代碼較少考慮。

20.每當你看到有需要有機會就重構。編程是關於抽象的,你的抽象映射越接近問題域,代碼就越容易理解和維護。隨着系統的有機增長,需要改變結構以擴大它們的用例。系統越來越多的抽象和結構,如果不改變它們就成爲技術性的債務。它會是更加痛苦的工作(更慢,越來越多的錯誤)。在特性開發估計中請考慮清除技術債務(重構)的成本。你遺留債務的時間越長,積累的利息就越高。關於重構和和測試的一本很棒的書是 Michael Feathers 的《修改代碼的藝術》(《Working Effectively with Legacy Code》)。

21. 代碼正確爲第一位,速度快第二位。在處理性能問題時,在修復錯誤之前先要做性能剖析。通常瓶頸不是你認爲的那樣。如果寫晦澀的代碼的唯一價值就是更快而且你已經做過性能剖析並證明了,那麼它實際就是值得的。編寫測試定期檢測你要做性能剖析的代碼,這樣可以很容易讓你知道你什麼時候測試過。測試可以留在測試套件中,以防止性能退化。(通常情況下,添加定時代碼總會改變代碼的性能特性,使性能工作成爲令人沮喪的任務之一。)

22. 當更小、範圍有限的單元測試失敗的時候,可以給出更多有價值的信息,告訴你具體是什麼錯誤。如果一個測試牽涉了半個系統來測試行爲,那麼它需要更多的調查以確定什麼是錯誤的。一般來說,運行超過 0.1 秒的測試不是單元測試。沒有所謂的慢單元測試。用限定範圍的單元測試測試行爲,你的測試行爲扮演了事實上的代碼規範。理想情況下如果有人想了解你的代碼,他們應該能夠把測試套件轉換爲行爲的“文檔”。Gary Bernhardt 的《快測,慢測》(《Fast Test, Slow Test》)是關於單元測試實踐的一篇很棒的演講。

23. ”非我所創“不像人們說的那麼壞。如果代碼是我們寫的,那麼我們知道它是什麼,我們知道如何維護它,在我們可以在適當的時候自由地擴展和修改它。這遵循了 YAGNI 原則:我們用那些適合我們需要用例的特定代碼,而不用我們不需要的可以做複雜事情的通用代碼。另一方面,代碼是敵人,擁有必要的代碼比擁有更多的代碼更好。引入新的依賴關係時要權衡。

24. 共享代碼所有權是我們目標;沉默的知識不是好知識。這意味着最低限度要討論或記錄設計決策和重要的實施決策。代碼評審(Code Review)是開始討論設計決策的最壞時刻,因爲在代碼編寫後,很難徹底更改。(當然在評審時指出並修改設計錯誤比沒有好。)

25. 生成器很棒!它們通常比迭代或重複執行的狀態對象更短更容易理解。David Beazley的《系統程序員的生成器訣竅》(《Generator Tricks for Systems Programmers》)是關於生成器一個很好的介紹。

26.讓我們成爲工程師!讓我們考慮設計、構建健壯並實現良好的系統,而不是做膨脹的有機怪物。然而編程是一種平衡。我們並不總是建造火箭。過度設計(洋蔥架構)同設計不完善的代碼一樣,處理起來非常痛苦。Robert Martin 的作品幾乎都值得一讀,《架構之潔:一個工匠的軟件結構和設計指南》(《Clean Architecture: A Craftsman’s Guide to Software Structure and Design》)是這個話題一個很好的資源。《設計模式》(《Design Patterns》)是每一位工程師都應該閱讀的經典編程書。

27. 間歇失敗的測試會侵蝕測試套件的價值,以至於最終每個人都忽略測試運行結果,因爲總有一些失敗的事情。修復或刪除間歇性失敗測試是痛苦的,但這些努力是值得的。

28. 一般來說,特別是在測試中,在需要等待一個特定的變化時候不要採用休眠隨機時間的方式。Voodoo(Python 庫) 的 sleeps 很難理解而且使你的測試套件變慢。

29. 至少讓你的測試失敗一次。故意加入一個錯誤,並確保它失敗,或在測試的行爲不完整的情況下運行測試。否則你不知道你真的在測試什麼。瞎寫的測試實際上不能測試任何東西或它很可能永遠不會失敗。

30. 最後一點:只關注特性改進是開發軟件的一種可怕方式。如果開發者的工作不能確保最好的結果,那麼不要讓他們爲自己的工作感到自豪。不處理技術債務會使開發變慢並最終導致產品更糟,問題更多。

感謝 Ansible 團隊,尤其是 Wayne Witzel,爲改善這個列表中的準則而提出的意見和建議。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章