優(yōu)雅編程之這樣寫測試用例,你就“正?!绷耍ň牛?/h1>

開心一笑

【朋友病了,要掛鹽水。給他扎針的是一個實習小護士,扎了半天都沒扎進血管。
他痛得齜牙咧嘴,無奈叫來了護士長。
護士長好手法,只見她一針見血地扎進了血管,然后馬上拔出來,
把針遞給那個實習護士說:“看清楚沒有?你再試一次!”】

提出問題

如何優(yōu)雅編寫測試代碼???

解決問題

開發(fā)工作當中,我們經(jīng)常需要編寫很多接口,但是往往忽略了單元測試。即使使用了單元測試,對于大部分人來說,也是用過既仍。編寫一些簡潔,可復用的單元測試時非常有必要的,以下是來自《代碼整潔之道》的幾點總結(jié):

1)TDD(測試驅(qū)動開發(fā)),意思是先寫單元測試,然后寫對應的代碼,通過修改調(diào)試讓寫的代碼通過單元測試。使用TDD,會使測試覆蓋所有的代碼,測試代碼和生產(chǎn)代碼的比例有可能會達到1:1 ,所以也會帶來成本的問題,所有我們要保持測試的整潔。

2)單元測試的好處:讓代碼可擴展,可維護,可復用。

3)整潔測試的三要素 :可讀性、可讀性、可讀性。

4)每個測試都可以拆分為三個環(huán)節(jié):構(gòu)造測試數(shù)據(jù)、操作測試數(shù)據(jù)、檢驗操作是否達到預期結(jié)果。

5)雙重標準:測試環(huán)境中和生產(chǎn)環(huán)境中有些條件不必完全一致。生產(chǎn)環(huán)境中有時要考慮內(nèi)存、CPU等性能問題,而在測試環(huán)境中不必做這些限制。

6)一個測試一個斷言,不必完全糾結(jié),但單個測試斷言數(shù)應該最小化,只測試一個概念,還是單一職責的問題;

7)F.I.R.S.T原則

  • F Fast:測試需要頻繁運行,因此要能快速運行;

  • I Independent:測試應該相互獨立;

  • R Repeatable:測試應當能在任何環(huán)境中重復;

  • S Self-validating:自足驗證,測試應該能看到成功與否的結(jié)果;

  • T Timely:測試應該及時編寫,應該恰好在生產(chǎn)代碼之前編寫;

下面用一個例子說明上面的理論(理論很枯燥,實例很美好):

package com.hwy.test;

import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;

public class CodeCleanTest {

    @Test
    public void testBuySnacks(){
        List<String> snacks =  buySnacks();
        Assert.assertEquals("購買零食有問題,請檢查!!!",true,snacks.size()>0);
    }

    @Test
    public void testEatSnacks(){
        /** 這個是我們自己構(gòu)建的零食數(shù)據(jù),肯定不會出現(xiàn)null等問題 **/
        /** 我們不要調(diào)用buySnacks方法,因為還是單一職責的問題,只測試一個概念**/
        List<String> snacks =  getSnacks();
        Assert.assertEquals("沒有一起吃3個零食", 3, eatSnacks(snacks));
    }

    @Test
    public void testDropLitter(){

        List<String> snacks =  getSnacks();
        Assert.assertEquals("垃圾沒有扔掉!!!",true,dropLitter(snacks));
    }

    @Test
    public void testDateWithGirl() throws Exception{
        Assert.assertEquals("約會失敗了", true, dateWithGirl());
    }

    /**
     * 構(gòu)建零食數(shù)據(jù)(事實這些注釋都是不需要,只是為了大家理解)
     * @return
     */
    public List<String> getSnacks(){
        List<String> snacksList = new ArrayList<>();
        snacksList.add("牛奶");
        snacksList.add("巧克力");
        snacksList.add("土豆片");
        return snacksList;
    }

    /**
     * 買零食(事實這些注釋都是不需要,只是為了大家理解)
     * @return
     */
    public List<String> buySnacks(){
        List<String> snacksList = new ArrayList<>();
        snacksList.add("牛奶");
        snacksList.add("巧克力");
        snacksList.add("土豆片");
        /** 這里故意顯示為空 **/
        //return null;
        return snacksList;
    }

    /**
     * 約會
     */
    public boolean dateWithGirl() throws Exception {
        boolean isSuccess = false;
        List<String> snacksList =  buySnacks();
        /** 利用逆向思維,拋出一個業(yè)務異常,這里我只是用簡單的exception代替 **/
        if(null == snacksList || snacksList.size() ==0){
            throw new Exception("你沒買到零食或買到的零食有問題,請檢查!");
        }
        /** 代碼執(zhí)行到這一步就說明snacksList不為null,之后的所有
         * 操作都不用判斷snacksList是否為空 **/
        List<String> litter =  eatSnacks(snacksList);
        dropLitter(litter);
        isSuccess = true;
        return isSuccess;
    }



    /**
     * 吃零食(事實這些注釋都是不需要,只是為了大家理解)
     * @param snacks
     */
    public List<String> eatSnacks(List<String> snacks){
        for (String snack : snacks) {
            System.out.println("一起吃" + snack);
        }
        return snacks;
    }

    /**
     * 仍垃圾(事實這些注釋都是不需要,只是為了大家理解)
     * @param litter
     */
    public boolean dropLitter(List<String> litter){
        boolean isDrop = false;
        for(String snack:litter){
            System.out.println("扔掉垃圾:" + snack);

        }
        /** 這里故意寫的有點冗余,只是方便大家學習 **/
        isDrop = true;
        return isDrop;
    }


}

上面的每個測試方法都是:test + 原方法名稱,一般測試方法都是這么命名的。同時上面的測試方法,每個方法都是獨立的,只測試一個概念。

下面舉一個錯誤的實例:

package com.hwy.test;

import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;

public class CodeCleanTest {

    @Test
    public void testBuySnacks() throws Exception{
        /** 像這樣子,在一個測試方法中,測試太多的方法,造成測試用例難以復用 **/
        /** 這樣不符合每個測試一個斷言的規(guī)則,同時也不符合F.I.R.S.T原則**/
        List<String> snacks =  buySnacks();
        Assert.assertEquals("購買零食有問題,請檢查!!!",true,snacks.size()>0);

        Assert.assertEquals("沒有一起吃3個零食", 3, eatSnacks(snacks));

        Assert.assertEquals("垃圾沒有扔掉!!!",true,dropLitter(snacks));

        Assert.assertEquals("約會失敗了", true, dateWithGirl());
    }


    /**
     * 構(gòu)建零食數(shù)據(jù)(事實這些注釋都是不需要,只是為了大家理解)
     * @return
     */
    public List<String> getSnacks(){
        List<String> snacksList = new ArrayList<>();
        snacksList.add("牛奶");
        snacksList.add("巧克力");
        snacksList.add("土豆片");
        return snacksList;
    }

    /**
     * 買零食(事實這些注釋都是不需要,只是為了大家理解)
     * @return
     */
    public List<String> buySnacks(){
        List<String> snacksList = new ArrayList<>();
        snacksList.add("牛奶");
        snacksList.add("巧克力");
        snacksList.add("土豆片");
        /** 這里故意顯示為空 **/
        //return null;
        return snacksList;
    }

    /**
     * 約會
     */
    public boolean dateWithGirl() throws Exception {
        boolean isSuccess = false;
        List<String> snacksList =  buySnacks();
        /** 利用逆向思維,拋出一個業(yè)務異常,這里我只是用簡單的exception代替 **/
        if(null == snacksList || snacksList.size() ==0){
            throw new Exception("你沒買到零食或買到的零食有問題,請檢查!");
        }
        /** 代碼執(zhí)行到這一步就說明snacksList不為null,之后的所有
         * 操作都不用判斷snacksList是否為空 **/
        List<String> litter =  eatSnacks(snacksList);
        dropLitter(litter);
        isSuccess = true;
        return isSuccess;
    }



    /**
     * 吃零食(事實這些注釋都是不需要,只是為了大家理解)
     * @param snacks
     */
    public List<String> eatSnacks(List<String> snacks){
        for (String snack : snacks) {
            System.out.println("一起吃" + snack);
        }
        return snacks;
    }

    /**
     * 仍垃圾(事實這些注釋都是不需要,只是為了大家理解)
     * @param litter
     */
    public boolean dropLitter(List<String> litter){
        boolean isDrop = false;
        for(String snack:litter){
            System.out.println("扔掉垃圾:" + snack);

        }
        /** 這里故意寫的有點冗余,只是方便大家學習 **/
        isDrop = true;
        return isDrop;
    }


}
感悟

讀書感悟

來自《窮查理智慧書》

  • 忙碌的人們很少有閑客來訪;沸騰的鍋里絕不會落入蒼蠅。
  • 失意時,沒有人了解;得意時,不了解自己。
  • 憤怒總是有一定的理,但很少有好的道理。
  • 你可能有時犯大錯,是因為你認為自己永遠正確。
  • 一百個小偷,也偷不走一個人的臉皮,特別是當這個人沒有臉皮的話。
  • 人們經(jīng)常把自己看錯了,卻很少把自己忘記了。
  • 無人認錯的爭吵必然延續(xù)無盡。
  • 如果你的秘密不想被敵人知道,請先對你的朋友保密。
  • 教訓換來的一兩智慧,勝過書本一斤的知識。

其他

如果有帶給你一絲絲小快樂,就讓快樂繼續(xù)傳遞下去,歡迎轉(zhuǎn)載,點贊,頂,歡迎留下寶貴的意見,多謝支持!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,709評論 25 708
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,818評論 18 139
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,162評論 4 61
  • 項目史無前例的難找,最近在做明天的計劃,看看有沒有轉(zhuǎn)型的可能 現(xiàn)在的項目是基于easyUI+SSH的框架,好幾個大...
    雁門員外閱讀 201評論 0 0
  • 何謂強求,說白了就是死切白咧求著不屬于自己的東西,然后得不到后嚎啕大哭,怨天尤人,哭訴到:憑什么,到底是憑什么!其...
    cici吃閱讀 421評論 0 2