最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

干貨 | 精準(zhǔn)化測試原理簡介與實踐探索

2022-10-19 16:51 作者:愛測軟件測試  | 我要投稿

小時候大家應(yīng)該都玩過一個游戲,游戲很簡單,就是找不同,在規(guī)定時間內(nèi)兩幅圖直接的差異點找到就算贏,越快越好,就像下面這樣:

上面這個不同點想找很簡單,那么下面這樣的呢?

這個,確實有的人會說"我可以!" 。比如在綜藝節(jié)目"最強(qiáng)大腦"中,這群"變態(tài)"的非人類確實可以

反正我不行,我也不信你們看到文章這里的人可以~我只有最菜大腦

理論上,我們?nèi)娴臏y試覆蓋,肯定就就可以保證,那么我們先看下下面的代碼:

這是一份涉及訂單狀態(tài)的各種枚舉,每一個狀態(tài)的背后都有其業(yè)務(wù)邏輯,甚至還有交叉,假若按照笛卡爾積或者正交的方式來進(jìn)行用例設(shè)計與覆蓋,有。。。好多好多用例

  • 那么~你真的有那么多時間去全覆蓋嗎? 開發(fā):我改了點代碼,等會幫忙全面回歸一遍吧 測試:好的(*** bi~~ ***) 什么?自動化?Are you sure?

測試發(fā)展到如今,好像不會點自動化,都不好意思叫測試,簡歷上不寫點自動化都拿不出手,但是自動化真的是測試的銀彈不,做過的應(yīng)該深有感觸,自動化屬于一個奢侈品:

  • 開發(fā)正本

  • 維護(hù)成本

  • 如何使用

  • 用例的設(shè)計合理性

  • 新功能的滯后性

再者,你確定你真的覆蓋到了被測代碼?也就是相當(dāng)于魔方墻上的每個色塊,實際在黑盒測試的過程中很大程度上取決于測試人員的經(jīng)驗,主觀性很強(qiáng),這樣就很可能漏測,發(fā)布后出了問題就又要開撕了。。。

可能有的小伙伴會這樣覺得,有人告訴我們答案,也就是告訴我們魔方墻的差異之處。這樣我不就知道關(guān)注的測試點了嗎?

沒錯,我們可以讓開發(fā)告訴我們本次改了哪些方法,甚至有代碼權(quán)限的情況下我們有能力可以自己去分析代碼,妥了,金女士!

那么問題又來了。針對上面的情況,開發(fā)的描述一定是正確全面的嗎?即使開發(fā)準(zhǔn)確的說明了改動的代碼,那么改動所影響到的其他范圍呢?開發(fā)本人也不好確認(rèn)的(不然還要測試干啥~),開發(fā)也有可能偷偷改代碼不告訴你呢。

這個時候就渴望有這么一個"最強(qiáng)大腦"

  • 眼過去就可以看出差異點(本次改動的邏輯)

  • 腦海中就有了差異的影響范圍(縮小需要測試的范圍)

  • 再一掃就看出哪些測試覆蓋到了(確認(rèn)測試覆蓋率)

以求達(dá)到一種精準(zhǔn)測試的程度

按照上面的描述,大概我們可以分為三個維度:

  • 差異化

  • 調(diào)用鏈

  • 覆蓋率 接下來的文章中會一個個詳細(xì)來說~

不同的語言,都會有對應(yīng)不同的語法分析器,語法分析器會把源代碼作為字符串讀入、解析,并建立語法樹,這是一個程序完成編譯所必要的前期工作。

我們看下 Java 的編譯過程,重點關(guān)注步驟一和步驟二:

這里我們使用一個簡單的Java對象,解析成AST后看下長什么樣子

由于層級太多太復(fù)雜,這里選取屬性user做個簡單演示說明。如下:

每一項里面都包含了最全面的信息,包括名稱、行號等,具體的可以訪問在線調(diào)試網(wǎng)站astexplorer.net/進(jìn)行調(diào)試查看

既然所有的代碼信息都有了,那么我們就可以拿著這些信息進(jìn)行比對,從而找出代碼的差異之處;(當(dāng)然這其中還是要很多降噪處理的,例如注釋、空格、業(yè)務(wù)無關(guān)代碼get/set等) 大概的流程邏輯如下

3.2.1 字節(jié)碼

因為Java代碼的運行,是通過javac先將Java文件編譯成.class結(jié)尾的字節(jié)碼,再由JVM去執(zhí)行;所以在字節(jié)碼文件中,擁有了足夠的元數(shù)據(jù)來解析類中的所有元素:類名稱、父類名、方法、屬性以及 Java 字節(jié)碼(指令);

以如下源碼為例:

1 ?public class AccurateTest {

2?

3 ? ? private int a = 1;?

4?

5 ? ? public String add(int b){?

6 ? ? ? ?return String.valueOf(a + b);?

7 ??

?}?

8?}

9

命令將其編譯為字節(jié)碼文件,再使用 命令將其反編譯后得到如下信息:

Classfile /Users/qinzhen/Documents/My/TrainingProject/calctest/src/test/java/AccurateTest.class ?Last modified 2021-7-15; size 386 bytes ?MD5 checksum e67842e9b540c556d288c28b303298fb ?Compiled from "AccurateTest.java" public class AccurateTest ?minor version: 0 ?major version: 52 ?flags: ACC_PUBLIC, ACC_SUPER Constant pool: ? #1 = Methodref ? ? ? ? ?#4.#19 ? ? ? ? // java/lang/Object."<init>":()V ? #2 = Fieldref ? ? ? ? ? #3.#20 ? ? ? ? // AccurateTest.a:I ? #3 = Class ? ? ? ? ? ? ?#21 ? ? ? ? ? ?// AccurateTest ? #4 = Class ? ? ? ? ? ? ?#22 ? ? ? ? ? ?// java/lang/Object ? #5 = Utf8 ? ? ? ? ? ? ? a ? #6 = Utf8 ? ? ? ? ? ? ? I ? #7 = Utf8 ? ? ? ? ? ? ? <init> ? #8 = Utf8 ? ? ? ? ? ? ? ()V ? #9 = Utf8 ? ? ? ? ? ? ? Code ?#10 = Utf8 ? ? ? ? ? ? ? LineNumberTable ?#11 = Utf8 ? ? ? ? ? ? ? LocalVariableTable ?#12 = Utf8 ? ? ? ? ? ? ? this ?#13 = Utf8 ? ? ? ? ? ? ? LAccurateTest; ?#14 = Utf8 ? ? ? ? ? ? ? add ?#15 = Utf8 ? ? ? ? ? ? ? (I)I ?#16 = Utf8 ? ? ? ? ? ? ? b ?#17 = Utf8 ? ? ? ? ? ? ? SourceFile ?#18 = Utf8 ? ? ? ? ? ? ? AccurateTest.java ?#19 = NameAndType ? ? ? ?#7:#8 ? ? ? ? ?// "<init>":()V ?#20 = NameAndType ? ? ? ?#5:#6 ? ? ? ? ?// a:I ?#21 = Utf8 ? ? ? ? ? ? ? AccurateTest ?#22 = Utf8 ? ? ? ? ? ? ? java/lang/Object { ?public AccurateTest(); ? ? ? ? ? ?//構(gòu)造函數(shù) ? ?descriptor: ()V ? ?flags: ACC_PUBLIC ? ?Code: ? ? ?stack=2, locals=1, args_size=1 ? ? ? ? 0: aload_0 ? ? ? ? 1: invokespecial #1 ? ? ? ? ? ? ? ? ?// Method java/lang/Object."<init>":()V ? ? ? ? 4: aload_0 ? ? ? ? 5: iconst_1 ? ? ? ? 6: putfield ? ? ?#2 ? ? ? ? ? ? ? ? ?// Field a:I ? ? ? ? 9: return ? ? ?LineNumberTable: ? ? ? ?line 1: 0 ? ? ? ?line 3: 4 ? ? ?LocalVariableTable: ? ? ? ?Start ?Length ?Slot ?Name ? Signature ? ? ? ? ? ?0 ? ? ?10 ? ? 0 ?this ? LAccurateTest; ?public java.lang.String add(int); ? ? ? ?//方法名 ? ?descriptor: (I)Ljava/lang/String; ? ? ?//方法描述符(入?yún)⒑头祷刂殿愋? ? ? ? ? ? ? ? ? ? ?flags: ACC_PUBLIC ? ? ? ? ? ? ?//方法的訪問標(biāo)致 ? ?Code: ? ? ? ? ? ? ? ? ? ?//code開始 ? ? ?stack=2, locals=2, args_size=2 ? ? ? ? 0: aload_0 ? ? ? ? 1: getfield ? ? ?#2 ? ? ? ? ? ? ? ? ? ?// 引用常量池的值 Field a:I ? ? ? ? 4: iload_1 ? ? ? ? 5: iadd ? ? ? ? 6: invokestatic ?#3 ? ? ? ? ? ? ? ? ? ?// Method java/lang/String.valueOf:(I)Ljava/lang/String; ? ? ? ? 9: ireturn ? ? ?LineNumberTable: ? ? ? ? ? ? ?//行號表,將上述操作碼與.java中的行號做對應(yīng) ? ? ? ?line 6: 0 ? ? ?LocalVariableTable: ? ? ? ?Start ?Length ?Slot ?Name ? Signature ? ? ? ? ? ?0 ? ? ? 7 ? ? 0 ?this ? LAccurateTest; ? ? ? ? ? ?0 ? ? ? 7 ? ? 1 ? ? b ? I ? ? ?//本地變量 } SourceFile: "AccurateTest.java"

通過上述信息我們可以直觀的看到字節(jié)碼中包含了Java運行所需的所有信息,且JVM對于字節(jié)碼文件要求嚴(yán)格,必須按照固定的組成和順序,而這種特性也就適合利用訪問者模式對字節(jié)碼文件進(jìn)行修改;因此也就要介紹我們的調(diào)用鏈生成的核心技術(shù)?!狝SM

3.2.2 ASM

操作; API接口,每當(dāng) ,掃描到類注解就會回調(diào) 等; 方法來實現(xiàn)字節(jié)碼的讀取和插入,例如在做調(diào)用鏈分析時我們就用到了其 方法來對方法體內(nèi)的調(diào)用信息進(jìn)行過濾和提取

通過上述的信息進(jìn)行匹配橋接,我們就可以拿到調(diào)用鏈中的一系列父子節(jié)點,形成我們的方法調(diào)用鏈

大概的流程邏輯如下:

說到覆蓋率統(tǒng)計,就要介紹當(dāng)前在這個技術(shù)領(lǐng)域中占據(jù)主導(dǎo)地位的開源工具-jacoco jacoco使用總的來說和裝大象一樣,需要三步

  1. 對被測項目進(jìn)行字節(jié)碼插樁

  2. 覆蓋率數(shù)據(jù)的采集與導(dǎo)出

  3. 覆蓋率數(shù)據(jù)的統(tǒng)計與報告生成 下面我們對這三個步驟逐一拆解 插樁,其實就是安插監(jiān)控探頭,我們的一行行代碼就好比一條條馬路,代碼里的分支(if-else)就好比馬路上的各種支路岔道,而插樁就相當(dāng)于在每一條路的路口都裝上了一個探頭

如下就是在字節(jié)碼中插入探針信息的圖示:

jacoco的插樁模式有兩種:

  • on-the-fly模式(運行時插樁)

  • 通過配置-javaagent在啟動命令中,jacoco介入被測項目部署過程,將探針(探頭)插入class文件,探針不改變原有方法的行為,只是記錄是否已經(jīng)執(zhí)行。

  • 優(yōu)點:無需提前進(jìn)行字節(jié)碼插樁,無需考慮classpath 的設(shè)置。

  • 缺點:要修改JVM參數(shù),對環(huán)境的要求比較高,于一些無法修改啟動命令的場景不適用。

  • offline模式(編譯時插樁)

  • 在測試之前先對文件進(jìn)行插樁,生成插過樁的class或jar包,測試插過樁的class和jar包,生成覆蓋率信息到文件,最后統(tǒng)一處理,生成報告。

  • 優(yōu)點:屏蔽工具對虛擬機(jī)環(huán)境的依賴;

  • 缺點:需要提前侵入代碼;無法實時獲取覆蓋率,只能測試完成后停止項目后統(tǒng)一生成報告 選擇:

方式無須入侵應(yīng)用啟動腳本,再加上公司的運維和開發(fā)可以配合部署 啟動參數(shù),因此我們最終選擇 模式進(jìn)行插樁

3.3.2 覆蓋率收集與導(dǎo)出 看了上面的插樁原理,想必覆蓋率的收集也就很好理解了,依然是以監(jiān)控探頭為例,當(dāng)我們測試一行行代碼時,就相當(dāng)于開著車跑在一條條道路上,而每進(jìn)入一行代碼就像是開車進(jìn)入了一條道路,那么進(jìn)入的時候就會被監(jiān)控探頭拍攝記錄下來,也就知道你跑過哪條路了。 同理,覆蓋到一行代碼時,探針就會記錄下信息,最終也就知道了哪一行代碼被覆蓋到了

至于導(dǎo)出,覆蓋率的統(tǒng)計信息會通過暴露的服務(wù)端口(默認(rèn)6300)去獲取,導(dǎo)出一份以.exec結(jié)尾的文件,文件中包含了當(dāng)前的覆蓋率信息

通過對exec文件的解析,jacoco便可以獲取所有方法的探針信息,從而計算覆蓋率,并對代碼進(jìn)行染色輸出報告:

針對代碼的染色如下

  • 紅色:代表未覆蓋

  • 黃色:代表部分覆蓋,

  • 綠色:代表完全覆蓋 在實際的使用場景中,我們可能還更關(guān)注本次修改的代碼,測試的時候我們會重點測試本輪開發(fā)的新增和改動范圍,因此jacoco原生的功能就不能滿足了,jacoco原生統(tǒng)計的是全量的覆蓋率。

對于改動點,我們稱之為增量,所以我們對jacoco的源碼進(jìn)行了二次開發(fā),使其支持增量的覆蓋率統(tǒng)計,以滿足日常測試需求;對比上面全量的范圍,可以看到增量的統(tǒng)計范圍就明確了,數(shù)量就少了很多:

  • 大概的架構(gòu)邏輯如下:

開發(fā)修改了一個方法或者一個接口,那么這個接口可能被N個應(yīng)用去調(diào)用,一旦這個接口有問題,那么影響面是相當(dāng)大的;或者這個接口本身沒問題,但是上下游沒有兼容好,調(diào)用出了問題也是影響產(chǎn)品質(zhì)量的;所以這個也是我們測試關(guān)注的重點。 再者,我們?nèi)粘5臏y試有很大一部分比例是接口測試,包括自動化也是,接口自動化用例很多。那么如果可以通過調(diào)用鏈路找到本次修改所影響到的最上層的入口接口( 等),那么通過接口與用例的關(guān)聯(lián)關(guān)系,就可以推薦出本輪修改必須要執(zhí)行的用例,提高用例的精準(zhǔn)程度和更加明確的測試范圍。 還有,如果改動的接口沒有關(guān)聯(lián)的用例,或者用例執(zhí)行完以后覆蓋率不達(dá)標(biāo),那么也可以對用例進(jìn)行查漏,添加新的用例進(jìn)行覆蓋。

  • 優(yōu)點:方案相對成熟,業(yè)界有落地案例,實現(xiàn)難度尚可

  • 缺點:鏈路也是通過插樁監(jiān)控的,那么前提就是這條鏈路要走到了才會存在,這樣就有滯后性,新增加的代碼鏈路還沒有測試過,那這條鏈路自然也就拿不到了

聊到這里,基本上就把測試人員的靈魂3問給回答完畢了。關(guān)于精準(zhǔn)化測試,這里有幾個問題會困擾測試開發(fā)人員。這里給出一些建議,希望可以對讀者有所益處。 1、如果我的代碼覆蓋率達(dá)到100%了,是不是就可以說測試覆蓋完全了,質(zhì)量有保障了?

答:不是, 覆蓋率低,質(zhì)量一定沒有保障,但是覆蓋率高,只是保障的一個維度達(dá)到了。 這里我們只是知道了代碼被覆蓋了,但是代碼邏輯的正確性呢?精準(zhǔn)化是無法判斷的,要靠大家自己去斷言了。 再者,覆蓋到的代碼都是開發(fā)按照自己理解的業(yè)務(wù)邏輯寫的,如果他漏寫了一些需求邏輯呢?那這部分就不存在覆蓋的情況了。

2、我是不是每次都要保證所有的方法覆蓋率都達(dá)到100%?

答:不是,方法的覆蓋率要達(dá)到什么樣的一個值,不好直接下結(jié)論。有些代碼邏輯,好比一些異常的捕獲,這個異常的觸發(fā)場景很難,日常測試幾乎走不到,那么就是覆蓋不了,覆蓋率也就不可能達(dá)到100%。

3、根據(jù)問題2,既然達(dá)不到100%,那么我是不是設(shè)一個閾值,好比80%?90%?,達(dá)到這個閾值就可以了?

答:也不是,有些方法,它的代碼邏輯可能都是核心邏輯,其中的分支都需要覆蓋,缺少了就有漏測出Bug的風(fēng)險,且理論上都是可以通過測試覆蓋到的,那么這種方法就需要達(dá)到100%的覆蓋率。

4、那要怎么衡量覆蓋率的指標(biāo)?

答:一方面可以設(shè)定一個最低閾值,哪怕代碼有些邏輯走不到,也不會大面積并且占比很高,還是需要一個最低的覆蓋率保障; 再者,需要測試的同學(xué)根據(jù)自己測試的業(yè)務(wù)進(jìn)行情況劃分,具備codereview的能力和習(xí)慣,平臺僅作為一個輔助測試的工具; 最后,我們可以記錄下以往測試的覆蓋率,根據(jù)不同業(yè)務(wù)通過測試后的覆蓋率情況統(tǒng)計覆蓋率的趨勢,以歷史的覆蓋率數(shù)據(jù)為依據(jù)來設(shè)定閾值或監(jiān)控告警,如果覆蓋率低于往期正常的值,就進(jìn)行告警或者卡點


干貨 | 精準(zhǔn)化測試原理簡介與實踐探索的評論 (共 條)

分享到微博請遵守國家法律
大荔县| 梨树县| 顺义区| 蛟河市| 固阳县| 永寿县| 江陵县| 横峰县| 门头沟区| 深水埗区| 屯留县| 奉化市| 三台县| 塔城市| 新郑市| 开远市| 宿州市| 墨江| 永定县| 略阳县| 台州市| 奉节县| 怀柔区| 阳谷县| 新邵县| 蒲城县| 新民市| 梁山县| 宣恩县| 成都市| 镇安县| 朝阳县| 义马市| 新丰县| 贡嘎县| 吐鲁番市| 樟树市| 涡阳县| 哈尔滨市| 闸北区| 淳安县|