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>

        阿里開源新一代單元測試 Mock 工具!

        共 5700字,需瀏覽 12分鐘

         ·

        2020-12-27 00:58

        TestableMock是基于源碼和字節(jié)碼增強(qiáng)的Java單元測試輔助工具,包含以下功能:
        • 訪問被測類私有成員:使單元測試能直接調(diào)用和訪問被測類的私有成員,解決私有成員初始化和私有方法測試的問題

        • 快速M(fèi)ock任意調(diào)用:使被測類的任意方法調(diào)用快速替換為Mock方法,實(shí)現(xiàn)"指哪換哪",解決傳統(tǒng)Mock工具使用繁瑣的問題

        • 輔助測試void方法:利用Mock校驗(yàn)器對方法的內(nèi)部邏輯進(jìn)行檢查,解決無返回值方法難以實(shí)施單元測試的問題

        訪問私有成員字段和方法

        如今關(guān)于私有方法是否應(yīng)該做單元測試的爭論正逐漸消停,開發(fā)者的普遍實(shí)踐已經(jīng)給出事實(shí)答案。通過公有方法間接測私有方法在很多情況下難以進(jìn)行,開發(fā)者們更愿意通過修改方法可見性的辦法來讓原本私有的方法在測試用例中變得可測。

        此外,在單元測試中時(shí)常會需要對被測對象進(jìn)行特定的成員字段初始化,但有時(shí)由于被測類的構(gòu)造方法限制,使得無法便捷的對這些字段進(jìn)行賦值。那么,能否在不破壞被測類型封裝的情況下,允許單元測試用例內(nèi)的代碼直接訪問被測類的私有方法和成員字段呢?TestableMock提供了兩種簡單的解決方案。

        方法一:使用`@EnablePrivateAccess`注解

        只需為測試類添加@EnablePrivateAccess注解,即可在測試用例中獲得以下增強(qiáng)能力:

        • 調(diào)用被測類的私有方法(包括靜態(tài)方法)

        • 讀取被測類的私有字段(包括靜態(tài)字段)

        • 修改被測類的私有字段(包括靜態(tài)字段)

        • 修改被測類的常量字段(使用final修飾的字段,包括靜態(tài)字段)

        訪問和修改私有、常量成員時(shí),IDE可能會提示語法有誤,但編譯器將能夠正常運(yùn)行測試。(使用編譯期代碼增強(qiáng),目前僅實(shí)現(xiàn)了Java語言的適配)

        效果見java-demo示例項(xiàng)目DemoPrivateAccessTest測試類中的用例。

        方法二:使用`PrivateAccessor`工具類

        若不希望看到IDE的語法錯(cuò)誤提醒,或是在非Java語言的JVM工程(譬如Kotlin語言)里,也可以借助PrivateAccessor工具類來直接訪問私有成員。

        這個(gè)類提供了6個(gè)靜態(tài)方法:

        • PrivateAccessor.get(被測對象, "私有字段名") ? 讀取被測類的私有字段

        • PrivateAccessor.set(被測對象, "私有字段名", 新的值) ? 修改被測類的私有字段(或常量字段)

        • PrivateAccessor.invoke(被測對象, "私有方法名", 調(diào)用參數(shù)..) ? 調(diào)用被測類的私有方法

        • PrivateAccessor.getStatic(被測類型, "私有靜態(tài)字段名") ? 讀取被測類的靜態(tài)私有字段

        • PrivateAccessor.setStatic(被測類型, "私有靜態(tài)字段名", 新的值) ? 修改被測類的靜態(tài)私有字段(或靜態(tài)常量字段)

        • PrivateAccessor.invokeStatic(被測類型, "私有靜態(tài)方法名", 調(diào)用參數(shù)..) ? 調(diào)用被測類的靜態(tài)私有方法

        快速M(fèi)ock被測類的任意方法調(diào)用

        相比以往Mock工具以類為粒度的Mock方式,TestableMock允許用戶直接定義需要Mock的單個(gè)方法,并遵循約定優(yōu)于配置的原則,按照規(guī)則自動在測試運(yùn)行時(shí)替換被測方法中的指定方法調(diào)用。

        歸納起來就兩條:

        • Mock非構(gòu)造方法,拷貝原方法定義到測試類,增加一個(gè)與調(diào)用者類型相同的參數(shù),加@MockMethod注解

        • Mock構(gòu)造方法,拷貝原方法定義到測試類,返回值換成構(gòu)造的類型,方法名隨意,加@MockContructor注解

        具體的Mock方法定義約定如下:

        1. 覆寫任意類的方法調(diào)用

        在測試類里定義一個(gè)有@MockMethod注解的普通方法,使它與需覆寫的方法名稱、參數(shù)、返回值類型完全一致,然后在其參數(shù)列表首位再增加一個(gè)類型為該方法原本所屬對象類型的參數(shù)。

        此時(shí)被測類中所有對該需覆寫方法的調(diào)用,將在單元測試運(yùn)行時(shí),將自動被替換為對上述自定義Mock方法的調(diào)用。

        注意:當(dāng)遇到待覆寫方法有重名時(shí),可以將需覆寫的方法名寫到@MockMethod注解的targetMethod參數(shù)里,這樣Mock方法自身就可以隨意命名了。

        例如,被測類中有一處"anything".substring(1, 2)調(diào)用,我們希望在運(yùn)行測試的時(shí)候?qū)⑺鼡Q成一個(gè)固定字符串,則只需在測試類定義如下方法:

        //?原方法簽名為`String?substring(int,?int)`
        //?調(diào)用此方法的對象`"anything"`類型為`String`
        //?則Mock方法簽名在其參數(shù)列表首位增加一個(gè)類型為`String`的參數(shù)(名字隨意)
        //?此參數(shù)可用于獲得當(dāng)時(shí)的實(shí)際調(diào)用者的值和上下文
        @MockMethod
        private?String?substring(String?self,?int?i,?int?j)?{
        ????return?"sub_string";
        }

        下面這個(gè)例子展示了targetMethod參數(shù)的用法,其效果與上述示例相同:

        //?使用`targetMethod`指定需Mock的方法名
        //?此方法本身現(xiàn)在可以隨意命名,但方法參數(shù)依然需要遵循相同的匹配規(guī)則
        @MockMethod(targetMethod?=?"substring")
        private?String?use_any_mock_method_name(String?self,?int?i,?int?j)?{
        ????return?"sub_string";
        }

        完整代碼示例見java-demokotlin-demo示例項(xiàng)目中的should_able_to_mock_common_method()測試用例。(由于Kotlin對String類型進(jìn)行了魔改,故Kotlin示例中將被測方法在BlackBox類里加了一層封裝)

        2. 覆寫被測類自身的成員方法

        有時(shí)候,在對某些方法進(jìn)行測試時(shí),希望將被測類自身的另外一些成員方法Mock掉。

        操作方法與前一種情況相同,Mock方法的第一個(gè)參數(shù)類型需與被測類相同,即可實(shí)現(xiàn)對被測類自身(不論是公有或私有)成員方法的覆寫。

        例如,被測類中有一個(gè)簽名為String innerFunc(String)的私有方法,我們希望在測試的時(shí)候?qū)⑺鎿Q掉,則只需在測試類定義如下方法:

        //?被測類型是`DemoMock`
        //?因此在定義Mock方法時(shí),在目標(biāo)方法參數(shù)首位加一個(gè)類型為`DemoMock`的參數(shù)(名字隨意)
        @MockMethod
        private?String?innerFunc(DemoMock?self,?String?text)?{
        ????return?"mock_"?+?text;
        }

        3. 覆寫任意類的靜態(tài)方法

        對于靜態(tài)方法的Mock與普通方法相同。但需要注意的是,靜態(tài)方法的Mock方法被調(diào)用時(shí),傳入的第一個(gè)參數(shù)實(shí)際值始終是null

        例如,在被測類中調(diào)用了BlackBox類型中的靜態(tài)方法secretBox(),改方法簽名為BlackBox secretBox(),則Mock方法如下:

        //?目標(biāo)靜態(tài)方法定義在`BlackBox`類型中
        //?在定義Mock方法時(shí),在目標(biāo)方法參數(shù)首位加一個(gè)類型為`BlackBox`的參數(shù)(名字隨意)
        //?此參數(shù)僅用于標(biāo)識目標(biāo)類型,實(shí)際傳入值將始終為`null`
        @MockMethod
        private?BlackBox?secretBox(BlackBox?ignore)?{
        ????return?new?BlackBox("not_secret_box");
        }

        完整代碼示例見java-demokotlin-demo示例項(xiàng)目中的should_able_to_mock_static_method()測試用例。

        測試無返回值的方法

        如何對void類型的方法進(jìn)行測試一直是許多單元測試框架在悄悄回避的話題,由于以往的單元測試手段主要是對被測單元的返回結(jié)果進(jìn)行校驗(yàn),當(dāng)遇到方法沒有返回值時(shí)就會變得無從下手。

        從功能的角度來說,雖然void方法不返回任何值,但它的執(zhí)行一定會對外界產(chǎn)生某些潛在影響,我們將其稱為方法的"副作用",比如:

        1. 初始化某些外部變量(私有成員變量或者全局靜態(tài)變量)

        2. 在方法體內(nèi)對外部對象實(shí)例進(jìn)行賦值

        3. 輸出了日志

        4. 調(diào)用了其他外部方法

        5. … …

        不返回任何值也不產(chǎn)生任何"副作用"的方法沒有存在的意義。

        這些"副作用"的本質(zhì)歸納來說可分為兩類:修改外部變量調(diào)用外部方法。

        通過TestableMock的私有字段訪問和Mock校驗(yàn)器可以很方便的實(shí)現(xiàn)對"副作用"的結(jié)果檢查。

        1. 修改外部變量的void方法

        例如,下面這個(gè)方法會根據(jù)輸入修改私有成員變量hashCache

        class?Demo?{
        ????private?Map<String,?Integer>?hashCache?=?mapOf();

        ????public?void?updateCache(String?domain,?String?key)?{
        ????????String?cacheKey?=?domain?+?"::"?+?key;
        ????????Integer?num?=?hashCache.get(cacheKey);
        ????????hashCache.put(cacheKey,?count?==?null???initHash(key)?:?nextHash(num,?key));
        ????}

        ????...?//?其他方法省略
        }

        若要測試此方法,可以利用TestableMock直接讀取私有成員變量的值,對結(jié)果進(jìn)行校驗(yàn):

        @EnablePrivateAccess??//?啟用TestableMock的私有成員訪問功能
        class?DemoTest?{
        ????private?Demo?demo?=?new?Demo();

        ????@Test
        ????public?void?testSaveToCache()?
        {
        ????????Integer?firstVal?=?demo.initHash("hello");?//?訪問私有方法
        ????????Integer?nextVal?=?demo.nextHash(firstVal,?"hello");?//?訪問私有方法
        ????????demo.saveToCache("demo",?"hello");
        ????????assertEquals(firstVal,?demo.hashCache.get("demo::hello"));?//?讀取私有變量
        ????????demo.saveToCache("demo",?"hello");
        ????????assertEquals(nextVal,?demo.hashCache.get("demo::hello"));?//?讀取私有變量
        ????}
        }

        2. 調(diào)用外部方法的void方法

        例如,下面這個(gè)方法會根據(jù)輸入打印信息到控制臺:

        class?Demo?{
        ????public?void?recordAction(Action?action)?{
        ????????SimpleDateFormat?df?=?new?SimpleDateFormat("yyyy-MM-dd?hh:mm:ss?");
        ????????String?timeStamp?=?df.format(new?Date());
        ????????System.out.println(timeStamp?+?"["?+?action.getType()?+?"]?"?+?action.getTarget());
        ????}
        }

        若要測試此方法,可以利用TestableMock快速M(fèi)ock掉System.out.println方法。在Mock方法體里可以繼續(xù)執(zhí)行原調(diào)用(相當(dāng)于并不影響本來方法功能,僅用于做調(diào)用記錄),也可以直接留空(相當(dāng)于去除了原方法的副作用)。

        在執(zhí)行完被測的void類型方法以后,用InvokeVerifier.verify()校驗(yàn)傳入的打印內(nèi)容是否符合預(yù)期:

        class?DemoTest?{
        ????private?Demo?demo?=?new?Demo();

        ????//?攔截`System.out.println`調(diào)用
        ????@MockMethod
        ????public?void?println(PrintStream?ps,?String?msg)?{
        ????????//?執(zhí)行原調(diào)用
        ????????ps.println(msg);
        ????}

        ????@Test
        ????public?void?testRecordAction()?{
        ????????Action?action?=?new?Action("click",?":download");
        ????????demo.recordAction();
        ????????//?驗(yàn)證Mock方法`println`被調(diào)用,且傳入?yún)?shù)符合預(yù)期
        ????????verify("println").with(matches("\\d{4}-\\d{2}-\\d{2}?\\d{2}:\\d{2}:\\d{2}?\\[click\\]?:download"));
        ????}
        }

        項(xiàng)目地址

        開源地址:https://gitee.com/mirrors/TestableMock

        關(guān)注數(shù):10億+?文章數(shù):10億+
        粉絲量:10億+?點(diǎn)擊量:10億+

        ?


        懸賞博主專區(qū)請掃描這里


        喜愛數(shù):?1億+?發(fā)帖數(shù):?1億+
        回帖數(shù):?1億+?結(jié)貼率:?99.9%


        —————END—————



        喜歡本文的朋友,歡迎關(guān)注公眾號?程序員哆啦A夢,收看更多精彩內(nèi)容

        點(diǎn)個(gè)[在看],是對小達(dá)最大的支持!


        如果覺得這篇文章還不錯(cuò),來個(gè)【分享、點(diǎn)贊、在看】三連吧,讓更多的人也看到~

        瀏覽 28
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        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>
            先锋影音资源av| 五月天婷婷在线视频| 国产香蕉视频免费| 国产精品视频一区二区三| 黄片免费大全| 国产精品久久久久久久久久王安宇 | 久久无码一区二区三区| 北条麻妃在线观看| 欧美激情爱爱| 午夜AV电影| 牛牛AV在线| 91在线视频免费| 在线观看亚洲中文字幕| 影音先锋AV无码| 在线观看免费人成视频| 亚洲三级无码视频| 先锋影音资源站av每日资源在线 | 国产主播一区二区| 日韩无码一卡二卡| 国产成人小视频在线观看| 成人国产片女人爽到高潮| 久久永久免费精品人妻专区 | 在线免费看黄片| 欧美青青草| 強姧伦一区二区三区在线播放| 中文在线观看视频| 亚洲AV成人精品日韩在线播放| 久操人妻| 成人欧美视频| 91精品国产综合久久久蜜臀主演| 成人亚洲AV| 波多野结衣无码AV| 久草视频免费看| 日韩激情一区| 欧美亚洲日韩一区二区| 粉嫩av在线| 国产精品日韩高清北条麻衣| 丝袜三级片| 一区二区三区无码专区| 撸一撸av| 婷婷五月天色综合| 国产精品V日韩精品V在线观看 | 大香蕉网站视频| 91一级A片在线观看| a亚洲a| 亚洲经典免费视频| 免费看黄片视频| 成年人免费看视频| 国精品无码一区二区三区在线| 自拍偷拍福利视频网站| 精品成人Av一区二区三区| 精品久久久999| 国产精品探花熟女| 无码天天| 久久精品三级视频| 国产熟妇搡BBBB搡BBBB搡| 中文字幕成人网站中文字幕| 三级片网页| 日韩AⅤ无码一区二区三区| 色综合社区| 久福利| 996热re视频精品视频| 天天透天天干| 丁香六月激情婷婷| 国产麻豆传媒| 天天日人人| 久久特黄| 精品一区二区三区毛片| 婷婷手机在线| 丰臀肥逼高清视频电影播放| 天天看高清无码| 亚洲猛男操逼欧美国产视频| 亚洲婷婷三级成人网| 高颜值呻吟给力| 日韩东京热中文字幕| 国产高清做爱| www一个人免费观看视频www| 黄色成人网站免费在线观看| 一级A黄片| 青草国产| 天天干天天拍| 国产午夜视频在线观看| 蜜桃一区二区三区| 人人妻人人爱| 玩弄大乳乳妾高潮乳喷视频| 青青草免费在线视| 女同一区二区三区| 成人黄色在线观看视频| 中国操逼电影| 91国内产香蕉| 欧美性猛交XXXXⅩXX| 三级网址在线| 美女极度色诱图片www视频| 五月丁香六月婷婷综合| 国产—a毛—a毛A免费看图| 中文字幕精品无码一区二区| 在线观看你懂得| aⅴ视频| 一级无码毛片| 无码a区天堂| 91无码视频在线观看| 韩国一区二区三区在线观看| 色婷婷中文在线| 美女91小视频| 成人黄色电影在线| 伊人精品A片一区二区三区| 无码人妻精品一区二区三区蜜桃91| 国产一级婬片A片AAA樱花| 残忍另类BBWBBWBBW| 亚洲97| 无码内射视频| 亚洲日韩乱码在线| 亚洲自拍天堂| 国产精品伦子伦免费视频| 亚洲成人a| 欧美亚洲日韩在线观看| 天天看片天天爽| 精品资源成人| 3D动漫精品啪啪一区二区下载| 黄片一区二区| 午夜大香蕉| 成人一二区| 黄网站欧美内射| 北京熟妇搡BBBB搡BBBB电影| 欧美日韩在线视频一区| 日韩视频网址| 狼友视频在线免费观看| 精品国产乱码久久久久久郑州公司 | 欧美日韩午夜福利视频| 中国黄色一级A片| 五月丁香婷婷在线观看| 男人的天堂青青草| 国产成人精品麻豆| 国产一级片免费看| 学生妹一级J人片内射视频| 亚洲综合婷婷| 精品国产乱码一区二区| 免费看黄片视频| 色狠久| 国产av地址| 天天插天天狠天天透| 亚洲色图在线视频| 日韩无码人妻| 国产免费乱伦| 国产精品毛片VA一区二区三区| 精品一区三区| 国产成人免费视频在线| 国产一级aa| 亚洲网站在线| 欧美日韩国产尤物主播精品| 亚洲国产av一区| 免费国产黄色视频网站| 天天射夜夜操| 2025AV天堂网| 午夜国产在线观看| 人人妻人人澡人人爽久久av| 波多野结衣高潮| 九七在线视频| 毛片网站免费| 国产综合久久777777麻豆| 久久人妻熟女中文字幕av蜜芽| 激情淫荡少妇| 久久艹精品视频| 欧美性爱XXXX黑人XYX性爽| 91吴梦梦一区二区传媒| 一区二区三区在线免费观看| 97人人操| 蜜臀99| 午夜无码福利在线观看| 亚洲精品久久久久久久久蜜桃| 中文字幕777| 国产精品九九| 黄色大片AV| 91破处网站| 操美女大逼| 亚洲色一区二区| 亚洲视频一区二区三区| 北条麻妃无码精品AV怎么看 | 91无码人妻精品一区二区蜜桃| 亚洲国产婷婷香蕉A片| 性少妇| 欧美A级视频在线观看| 欧美日韩黄色极品| 北京熟妇搡BBBB搡BBBB| 91欧美视频| 一区二区视频免费| 国产免看一级a一片成人aⅴ| 亚洲成年网| 欧美在线黄片| 西西888WWW大胆无码| AV三级无码| 国产精品一区二区三区不卡| 黄色片大全| A视频在线观看| 精品视频在线观看| 婷婷中文| 黄色成人视频免费看| 亚洲无码电影网站| 中文字幕在线视频观看| 亚洲欧洲在线播放| 老太老熟女城中层露脸60| 91老熟女视频| 亚洲日韩一区二区| 中文字幕在线播放AV| 蜜桃视频app| 九九精品在线观看| 一道本在线观看| 91人妻一区二区| 亚洲高清无码视频大全| 午夜男人天堂| 黄色小电影网站| 天天爽夜夜爽AA片免费| 亚洲第一成人久久网站| 无码国产一区二区三区四区五区| 毛片一区二区三区| 日韩在线视频网| 国产成人自拍网| 尤物视频官网| 色欲av在线| 特级大毛片| 蜜桃传媒一区二区亚洲A| 欧美成人免费网站| 娇小,学生,高潮,videos| 国产在线你懂得| 国产一级a毛一级a毛视频在线网站)| 97国产免费| 成人视频三级| 国产欧美日韩综合在线视频 | 日本v片| 一级特黄妇女高潮AA片免费播放| 人妻无码在线视频| 久久无码区| 日本免费A∨| 97人妻天天摸天天爽天天| 日韩高清AV| 俺来也俺去| 青春草在线免费视频| 欧美一级片在线| 色香蕉视频在线观看| 无码人妻久久一区二区三区蜜桃 | 天天操人人爽| 熟女中文| 亚洲精品成人电影| 国产精品女人精品久久久天天| 久艹大香蕉| 亚洲高清AV| 黑人vs亚洲人在线播放| 国产精品久久久久久久久久久久久久久久| 欧美疯狂做受XXXXX高潮| 青草福利视频| 不卡无码中文字幕| 男女啊啊啊| 水蜜桃视频在线观看| 国产精品一区二区AV日韩在线 | 91久久性奴调教| 激情综合网站| 黄色AA片| 黑人中文字幕| 俺来俺也去www色在线观看| 国产黃色AAA片| 日韩在线1| 在线观看视频无码| 91精品丝袜久久久久久久久久粉嫩 | 国产资源在线观看| 国产亚洲视频完整在线观看| 91无码精品国产AⅤ| 国产在线拍偷自揄拍无码一区二区| 亚洲高清无码在线观看| 欧美99视频| 色视频在线| 91人妻人人澡人人爽人人精品| 人妻18无码人伦一区二区三区精品| 国产一级操逼视频| 亚洲精品一区二区二区的游戏情况 | 在线播放亚洲| 日韩视频在线观看一区| 精品欧美视频| 成人黄色在线视频| av天堂资源在线| 91人妻无码精品蜜桃| 麻豆91免费看| 真实白嫖91探花无码| 国产—a毛—a毛A免费| 国外亚洲成AV人片在线观看| 尿在小sao货里面好不好| 日本无码在线播放| 黄色视频在线| 一道本无码在线播放| 国产欧美熟妇另类久久久| 999成人网| 99人人操| 久久亚洲中文字幕乱码| 一级黄色视频片| 啪啪网站免费| 高清毛片AAAAAAAAA片| 欧美日韩成人在线观看| 日韩在线视频免费观看| www.午夜福利| 黄骗免费网站| 欧美精品18videosex性欧美|