1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        聊聊單元測試

        共 4012字,需瀏覽 9分鐘

         ·

        2020-11-15 05:46












        這里是Z哥的個人公眾號

        每周五11:45 按時送達

        當然了,也會時不時加個餐~

        我的第「167」篇原創(chuàng)敬上



        大家好,我是Z哥。

        提起單元測試,很多人對它的態(tài)度是,我知道它有用,但是我不想寫。大多數(shù)人的理由是沒時間寫,任務太多。

        但是說實話,是真的沒時間嗎?Z哥認為真是由于沒時間而不寫單元測試的人絕對是少數(shù)。況且,導致沒時間很大原因可能就是花了太多時間在處理bug上。


        所以,很多人沒有把單元測試當作一個“工具”,而把它看作是一種“負擔”。

        在這種心態(tài)下,就算要寫單元測試,也是為了寫而寫。更可怕的是,通過mock工具,還真能給任意代碼寫單元測試。

        但是這樣的做法其實是“買櫝還珠”,真正的浪費時間。


        最典型的情況是,很多人一開始寫測試代碼就錯了,看上去寫了很多Mock、Assert,但是到底想驗證什么,測試什么其實并不明白。一個不留神,測試代碼就變成驗證某個RPC接口對不對,某個第三方系統(tǒng)庫的函數(shù)對不對等等,這明顯就跑偏了。

        對于這種情況,無論多么牛逼的工具都幫不了你,只能提高自己對單元測試的理解。


        還有一種情況是,寫代碼的時候并沒有考慮這代碼要怎么測,因此寫完了以后發(fā)現(xiàn)寫單元測試很難,沒有現(xiàn)成的測試入口。這時候項目交付的deadline又快到了,唉,要不先放著改天再寫吧。當然我們都知道,這個改天大概率再也不會做。


        我們有一萬個理由可以不做單元測試。但是這就好比,組裝一架飛機不用測試各個零件的運作是否符合預期,直接讓它飛起來再看有哪些問題。

        以后如果誰說單元測試不重要的時候,你不妨問他“你敢坐沒有檢查過的飛機么?”


        另外,單元測試除了對軟件質(zhì)量有提升外,對軟件的開發(fā)效率提升也很明顯

        在《實用軟件度量》一書中提到了微軟內(nèi)部的統(tǒng)計數(shù)據(jù),單元測試的成本效率是系統(tǒng)測試的3倍。

        在《單元測試的藝術》中也提到過一個案例,找了開發(fā)能力相近的兩個團隊,同時開發(fā)相近的需求。進行單測的團隊在編碼階段時長增長了一倍,從7天到14天,但是,這個團隊在集成測試階段的表現(xiàn)非常順暢,bug量小,定位bug迅速等。最終的效果,整體交付時間和缺陷數(shù),均是單元測試團隊最少。


        單元測試還有一個好處,就是讓我們嘴上說的「高內(nèi)聚低耦合」的代碼有了一條統(tǒng)一的實現(xiàn)路徑。因為代碼到底算不算高內(nèi)聚低耦合,其實每個人的主觀標準都不同。但是是否容易做單元測試,這卻是一個相對更客觀的標準。

        所以,如果有人跟你說他這段代碼設計得非常好,但就是不好寫單元測試,相信你知道該怎么做了:D


        那么,正兒八經(jīng)的單元測試應該怎么寫呢?我來分享一些我的經(jīng)驗和思考,希望能讓更多的人參與到編寫單元測試的隊伍中來。


        /01 ?怎么才算“單元”?/

        相信很多人和Z哥一樣,剛接觸單元測試的時候覺得單元測試就是用來測某個方法的。其實并不是這樣,這里的「單元」如何定義取決你如何定義“一件事”。只要這個「單元」里做的是“同一件事”,那么哪怕其中包含了3個方法,它也可以是一個「單元」。

        比如,你寫了一個下訂單的單元測試,你可以把生成訂單方法和扣減紅包方法放在一起做單測,這樣比兩個方法分別做單測還可以多做一些關聯(lián)驗證。比如,訂單上的紅包金額是否與扣減的紅包金額一致?


        /02? 如何判斷單元測試的好壞/

        單元測試和大多數(shù)技術工作不同,寫得越好的單元測試往往用到的工具越簡單,甚至不需要額外的工具。

        在我的概念里,單元測試的好壞分為以下幾個等級。

        第一級,大部分代碼不需要 Mock 就可以測試。這是最優(yōu)秀的。
        第二級,大部分代碼需要Mock才能測試,但都不是靜態(tài)方法。
        第三級,大部分代碼需要Mock才能測試,而且包含大量靜態(tài)方法。(一般的Mock工具還無法Mock靜態(tài)方法)

        可能你會有疑問,為什么Mock靜態(tài)方法是不好的?這個后面講具體做法的時候會說。


        說了這么多,具體怎么寫呢?寫單元測試其實就是做以下三件事。


        /01 ?確定寫單元測試的范圍/

        做任何的事都得回歸到價值本身,單元測試也是如此。比如,你給一個固定返回字符串“Hello World”的方法寫單元測試就是一個浪費時間的事情。

        一般來說,哪些類型的代碼適合寫單元測試?

        1. 公用組件庫。這些代碼變更不會特別頻繁,所以覆蓋率需要盡量達到100%。

        2. 被調(diào)用頻次越高的代碼。



        /02 ?怎么寫?/

        具體怎么寫其實就是確定你要通過代碼驗證的東西是什么。這里你可以根據(jù)以下這4個標準來,不同重要度的方法,可以選擇適合的標準來寫。

        • L1:輸入正確的參數(shù)時,會有正確的輸出。(測試正確的處理邏輯是否符合預期)

        • L2:輸入錯誤的參數(shù)時,不能拋出系統(tǒng)級的異常。(測試錯誤的處理邏輯是否符合預期)

        • L3:極端情況和邊界數(shù)據(jù)可用??赡芤婚_始無法考慮到很多邊界條件和極端情況,所以這是一個需要長期維護的部分。

        • L4:覆蓋率達到100%。



        Z哥我對這4個標準的運用場景是:

        • L1,實在時間緊迫并且代碼對應的功能不是核心部分。

        • L2,非核心模塊大部分時候應該要達到的標準。

        • L3,核心模塊要達到的標準。

        • L4,全局基礎框架、封裝的非業(yè)務型類庫要達到的標準。



        /03 ?單元測試的數(shù)據(jù)從哪來?/

        很多人覺得寫單元測試麻煩,主要的原因就是覺得構(gòu)造測試數(shù)據(jù)費時間。所以,取巧的方法是直接連到DB,基于DB里的數(shù)據(jù)做單元測試。

        但是這樣的數(shù)據(jù)是不穩(wěn)定的,一旦某個前置方法的邏輯有問題,導致數(shù)據(jù)庫里的數(shù)據(jù)出現(xiàn)異常,那么后續(xù)的測試方法都會連續(xù)出錯。

        所以我認為單元測試的測試數(shù)據(jù)應該人為地在測試代碼里構(gòu)造。如此不但能讓數(shù)據(jù)變得穩(wěn)定,而且單元測試的運行效率也會更高,畢竟少了多次連接數(shù)據(jù)庫的操作。

        《Google軟件測試之道》中提到谷歌的做法也是如此。在谷歌,單元測試被劃分為「小型測試」類型,對于小型測試的特點就是不需要外部依賴,所以涉及到的外部服務需通過Mock或Fack來實現(xiàn)。(Mock、Fake、Stub都是單元測試中的基本概念,可以自行搜索了解)


        再分享兩個最佳實踐給你,讓你可以更容易編寫單元測試。


        /01/

        涉及到I/O的代碼和業(yè)務代碼盡量分開。這里的I/O不僅僅是磁盤I/O還有網(wǎng)絡I/O。

        Pascal之父——Niklaus Wirth提出過一個著名的公式:程序 = 算法 + 數(shù)據(jù)。數(shù)據(jù)的操作和獲取就是通過I/O進行的,一旦剝離后,剩下的代碼就是算法,也就是“邏輯”,我們寫單元測試要驗證的恰好就是它。

        實現(xiàn)方式也很簡單,將I/O部分抽象出接口,通過依賴注入方式調(diào)用。這樣你在寫單元測試的時候可以通過Mock方式來提供一個I/O方法的實現(xiàn)。


        /02/

        測試數(shù)據(jù)與用例分離。在你寫單元測試的時候,因為需要考慮很多種情況,所以需要構(gòu)造好幾套測試數(shù)據(jù)。

        為了便于管理和維護這些數(shù)據(jù),你可以避免將數(shù)據(jù)與單元測試代碼寫在一起。舉個例子,你可能平時是這么寫的。

        @Test
        Public void testAdd(){
        assertEquals(expect: 2, MethodAdd(a: 1,b: 1));
        assertEquals(expect: 0, MethodAdd(a: -1,b: 1));
        assertEquals(expect: 0, MethodAdd(a: 0,b: 0));
        }

        以后你可以試試這樣寫:

        @Test
        void testAdd(){
        ? ? ? ? for(Object[] s : data()){
        ? ? ? ? ? ? assertEquals(s[2], (int)s[0]+(int)s[1]);
        ? ? ? ? }
        }

        public static Iterable data(){
        ? ? ? ? ? ? List list = new ArrayList();
        ? ? ? ? ? ? list.add(new Object[]{1,1,2});
        ? ? ? ? ? ? list.add(new Object[]{-1,1,0});
        ? ? ? ? ? ? list.add(new Object[]{0,0,0});
        ? ? ? ? ? ? return list;
        }

        這樣,后續(xù)維護測試參數(shù)只要在data()方法里進行就好了(當然你也可以使用junit之類的工具來簡化這個寫法)。畢竟做單元測試是一件長期的事情,需要根據(jù)新發(fā)現(xiàn)的bug保持測試數(shù)據(jù)的更新,以確保已發(fā)生的bug總是被覆蓋在單元測試范圍內(nèi)。


        另外,易于做單元測試的代碼,其實它的性能也會不錯。因為耗時的I/O操作不會隱藏在各個方法里,讓你無意間就重復調(diào)用了。相反,你可以直觀的看到每個方法里有哪些I/O操作,能合并請求的可以在調(diào)用這些方法之前合并掉。


        好了總結(jié)一下。

        這篇呢,Z哥和你分享了我對寫單元測試這件事的看法。

        首先,我們應該把它當作“工具”而不是“負擔”。因為單元測試除了可以提升軟件質(zhì)量,還可以提高開發(fā)效率,以及優(yōu)化代碼設計。

        然后,實際在做的時候,我從「確定寫單元測試的范圍」、「怎么寫?」、「單元測試的數(shù)據(jù)從哪來?」三個方面給了我的建議。并且分享了兩個有效的最佳實踐給你。

        以我的親身經(jīng)歷告訴你,當你每次改完代碼run一遍單元測試,看到那些success和failurel列表的時候,你會覺得“真香”。不信你試試。


        如今,好像一個團隊不說自己在敏捷開發(fā)就落伍了。然而類似于測試驅(qū)動開發(fā)(TDD)之類的開發(fā)方式恰恰是敏捷開發(fā)實踐的重要組成部分,但是我們卻嫌棄它拖慢迭代速度。

        那么我們到底是不是在敏捷呢?


        推薦閱讀:


        原創(chuàng)不易,如果你覺得這篇文章還不錯,就「在看」或者「分享」一下吧。鼓勵我的創(chuàng)作 :)


        如果你有關于軟件架構(gòu)、分布式系統(tǒng)、產(chǎn)品、運營的困惑

        可以試試點擊「閱讀原文

        瀏覽 32
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            插插插啊啊啊 | 女人打开腿被男人狂桶30分钟 | 亚洲AV无码成人精品区 | 综合婷婷久久丁香五月天 | 天天操天天操天天操天天操天天操 | 国产aaa大片 | 韩国一级片免费观看 | 欧美人妖乱伦 | 一级黄色一级黄色 | 性中国xxxxsss3 |