公司最大的內卷,偷偷做單元測試
原創-
2024-08-13 10:00:00
-
2025
本篇目錄
一位讀者在看過我的《理解這八大優勢,才算精通單元測試》後,問我:知道單元測試有好處,但實在沒空寫。看完文章後又想重新落實一下,有沒有啥寫好單元測試的技巧?
這位讀者絕對不是第一個和我抱怨單元測試的人。這很好理解,中國互聯網公司太多太卷,想要搶奪市場就要推出不同功能,而這些壓力一部分落在了程序員身上,拼命趕需求。單元測試這種費力不討好的事情,自然而然就沒有人做。
就我多年的經驗來看,寫單元測試其實不會拖延項目,反而能夠加快功能研發進度。單元測試的好處我就不在這裏贅述了,只有真正嘗試過的人才能理解。
馬克·吐溫曾說:“取得成功的秘訣就是開始”。本篇文章想和大家分享一下寫好單元測試的技巧,希望可以給大家帶來新方向。
一、單元測試的注意事項
單元測試是爲了讓我們快速查找並隔離損壞的代碼片段。正因如此,這些函數和類在測試時不應該依賴于mock(模擬)和stub(存根)以外的其他元素。在測試中,如果試圖覆蓋的邏輯過于複雜,就難以確保覆蓋的可靠性,也難以准確找出失敗的原因。
因此,我們要注意單元測試包括以下幾點。
01 简洁性
短函數更容易閱讀和理解。我們每次只測試一個邏輯點,因此測試代碼應該控制在幾行之內。但如果是高級邏輯可能具有多個依賴項,這就需要大量樣板代碼來初始化模擬和存根。此外,單元測試同樣適用DRY原則(Don’t repeat yourself,一次且仅一次),我們在寫單元測試時要避免到處複制粘貼混亂的代碼,最好使用組合而不是繼承。
02 明确性
單元測試要使用詳盡的長名稱。這樣的名稱不僅能清楚表達信息,還能起到索引作用、快速定位相應測試。就算需求發生變化,我們只需要針對相應的測試進行更改,不必查看所有內容並檢查受影響的內容。
好的單元測試一般只有一個斷言,因此命名起來也很容易。例如,在處理金額計算時,it('should return 0 for an empty cart')
要比it('works for 0')
或者 it('empty cart')
好得多。對于使用函數名稱作爲測試名稱的框架也是如此,shouldReturnZeroForAnEmptyCart
就是一個很不錯的的命名。
正如丁玲所言:“人生就像爬坡,要一步一步來。”單元測試也是如此,不要一次性測試整個方法,要一步一步來。我們只針對單個需求寫單元測試,代碼就會變得易于閱讀和維護。
03 可维护性
測試框架需要提供各種斷言方法。它們提供不同的方法來檢查結果,並且當斷言失敗時,它們還會顯示更具體的錯誤消息,從而提供更多上下文來查看錯誤所在。
例如,
expect(result === expected).toBeTruthy();
將會失敗
expect(received).toBeTruthy() Received: false
盡管
expect(result).toBe(expected);
將提供更多有關具體失敗原因的信息:
expect(received).toBe(expected) // Object.is equality Expected: "John Doe" Received: "JohnDoe"
框架還爲不同的測試方式提供了各種斷言。例如,在使用Jest進行測試時,toBe
使用Object.is
測試是否完全相等,而toEqual
和toStrictEqual
則深入比較對象,確保他們的類型和結構一致。
toBeCloseTo
。雖然toEqual
有時也能適用,但即使是看似簡單的測試,如expect(0.1+0.2).toEqual(0.3)
也可能無法通過。
二、單元測試的AAA原則
遵循AAA原則(Arrange、Act、Assert,安排、執行、斷言),可以娴熟提升單元測試代碼的清晰度、可靠性和可維護性。
第一步,安排階段(Arrange)。我們需要完成變量賦值、對象實例化對象以及測試運行所需的其余前置設置,並且定義預期結果。這樣做的好處在于:一方面,我們需要在執行測試邏輯前就有明確預期;另一方面,這更方便在輸入數據後立即查看預期輸出,有助于避免代碼混淆。
第二步,執行階段(Act)。我們將執行測試函數並存儲其結果。結果存儲其實是准備工作的自然延伸,有助于我們對結果進行回顧總結。
第三步,斷言階段(Assert)。我們在這個階段可以判斷假設的正確性了。這正是單元測試的核心所在,因爲這一環節實際上是對某些具體內容的測試。其目的在于是檢查實際得到的結果否與預期結果相匹配。
我们要确保代码可靠性,避免错误输入、缺少参数、空数据、调用函数中的异常等情况的出现。代码覆盖率工具可以幫助我们查漏补缺,找到未测试的代码分支。我們要始終明確我們單元測試的目標,過于追求100%測試覆蓋率反而會讓單元測試代碼越來越繁雜。這與《呂氏春秋》中的論點不謀而合:“不知輕重,?則重者爲輕,?輕者爲重矣。?若此,?則每動無不敗”。
三、單元測試的優化和維護
爲了提高單元測試效率,我們需要模擬所有可能影響速度的外部依賴項,例如API調用、數據庫或文件系統訪問。我們在寫單元測試時,應盡量避免線程休眠、等待和超時。如果必須設置超時,就應該將其縮短至幾毫秒。在處理多線程或異步競爭條件時,精確控制出發條件比簡單的等待要有效得多。
單元測試應當確保不會改變作用域外的任何內容。如果測試僅在按照特定順序執行時才能成功,這可能表明測試用例或測試代碼存在問題。每個測試用例應獨立運作。由于現代測試框架默認並行執行測試,因此我們不應依賴全局變量或之前測試的遺留效應。這也是全局變量常被視爲不良編程習慣的原因之一,這會隱藏真正的依賴關系,導致代碼耦合度升高,並在處理多線程問題時需要格外留意。
當測試需要複雜的重複配置時,應利用框架提供的設置和清理功能。這些功能保障了在每個測試用例或整個測試套件開始前後,相關代碼能夠得到執行。這樣,無論是單獨運行測試還是作爲測試套件的一部分,都能確保測試結果的確定性,執行順序不會對測試結果造成影響。
四、單元測試貴在堅持
《荀子·大略》:“夫盡小者大,積微成著,德至者色澤洽,行盡而聲問遠。”單元測試的作用只有經過長期積累才會變得顯著。其實,寫單元測試更多的是對自己的代碼負責。有測試用例的代碼,別人更容易看懂,以後別人接手你的代碼時,也可能放心做改動。
-
禅道産品
禅道開源版 禅道企業版 禅道旗艦版 禅道IPD版 -
核心功能
産品管理 項目管理 質量管理 效能管理 -
使用文檔
基本版手冊 企業版手冊 旗艦版手冊 IPD版手冊 開發中心手冊 -
幫助中心
积分問答 常見問題 論壇交流 使用視頻 Gitee GitHub -
關于我們
關于我們 禅道軟件 最新動態 禅道活動 -
禅道社區
禅道博客 積分排行 積分商城 禅道書院 -
聯系方式
聯系人:高麗亞 電話:17667930330 微信:17667930330 Q Q:3645260865北京、上海、深圳分部