第二部分,原理和方法。我們剛才也給大家普及了基礎(chǔ)的東西,這部分的training set,都是來(lái)自于實(shí)際中編寫(xiě)的代碼或者是樣例,是作為Bug預(yù)防的第一代原形,相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,只用了一個(gè)圈復(fù)雜度,知道圈復(fù)雜度的同學(xué)舉一下手,為什么可以作為預(yù)測(cè)Bug出現(xiàn)概率的重要指標(biāo)?就是圈復(fù)雜度會(huì)根據(jù)你寫(xiě)的代碼,里面有多少分支,走多少流程,跟圈復(fù)雜度是對(duì)照的。一個(gè)函數(shù)里面,如果圈復(fù)雜度,工業(yè)級(jí)別上的,好比軍工的軟件,一般檢測(cè)出來(lái)不超過(guò)7,或者更嚴(yán)格的,代碼復(fù)雜度不能超過(guò)5,當(dāng)圈復(fù)雜度越高的時(shí)候,就證明寫(xiě)的函數(shù)體越來(lái)越長(zhǎng),但是如果在5和7以內(nèi),大概40行左右,超過(guò)7以上,到20了,代碼行數(shù)就有可能變成幾千行了,幾千行的函數(shù),無(wú)論是讓原來(lái)的開(kāi)發(fā),還是接手的新開(kāi)發(fā)來(lái)去做這個(gè)事情,都變得很難維護(hù)。既然是我們能知道圈復(fù)雜度有這樣的功能,我們可以用圈復(fù)雜度來(lái)預(yù)測(cè)提測(cè)代碼之后,去掃一下圈復(fù)雜度,看是不是在合理的范圍內(nèi)之內(nèi),如果不在合理的范圍之內(nèi),可能會(huì)有一定的概率出現(xiàn)Bug。
這個(gè)圖里面介紹了學(xué)術(shù)界應(yīng)用,把加入的代碼出現(xiàn)的Bug解決掉,做了一個(gè)樣本,沒(méi)有修Bug之前代碼是怎么寫(xiě)的,修完Bug之后代碼又是如何寫(xiě)的。
現(xiàn)場(chǎng)視頻(中)
這是基于Bug定位出現(xiàn)的第二個(gè)原形,用來(lái)預(yù)測(cè)Bug缺陷。從第一個(gè)原形到第二個(gè)原形,中間已經(jīng)多了很多步驟,首先需要有一個(gè)dataset。第二是前面說(shuō)的學(xué)術(shù)界的獲取dataset的方法。第三是用人工自然語(yǔ)言的去處理,不用java管理的,或者不跟javaBug關(guān)聯(lián)的,但是有很多開(kāi)發(fā)去直接寫(xiě),用英文也好或者用中文注釋也好,會(huì)說(shuō)某某個(gè)問(wèn)題,這是一般開(kāi)發(fā)都會(huì)做的。第三個(gè)是獲取樣本的方法。將來(lái)可能出現(xiàn)Bug的因素有哪些,這是這些因素做了排序和處理,做Bug定位或者預(yù)測(cè)的時(shí)候,首先得知道哪個(gè)指標(biāo)應(yīng)該占什么樣的比例,用剛才的方法,就去不斷調(diào)整各種指標(biāo)占的比例,然后根據(jù)所占的這些比例多少,判斷實(shí)際得到的和最終現(xiàn)實(shí)發(fā)生的真實(shí)的Bug匹配度,在人工智能中有一個(gè)專業(yè)名詞是召回率,來(lái)看最終的結(jié)果,這是屬于第二代的原形。在這個(gè)原形里面,有一個(gè)test的樣本,大部分是使用變異測(cè)試來(lái)做的。
這是基于java的,參考各種因素和指標(biāo),能得到一些樣例結(jié)果的展示。每種定位技術(shù),得到的能定位的結(jié)果差異也是有很多的,如果大家想去看具體的實(shí)現(xiàn),在這個(gè)底下有一個(gè)連接,把種種技術(shù),包括方法,源碼,結(jié)果,都生成一個(gè)統(tǒng)計(jì)結(jié)果,可以到里面看具體的執(zhí)行情況是什么樣的。
這是堆棧,現(xiàn)在的堆棧拋出,一般情況下,無(wú)論是前端也好,還是后端也好,都要求封裝。Exception,堆棧有多個(gè)方法,異常的會(huì)提示說(shuō)屬于什么樣的異常,既有基于系統(tǒng)級(jí)別的,也有中間件定義的,也有用戶行為定義出來(lái)的,這個(gè)時(shí)候我們不能單純的說(shuō)用它做缺陷預(yù)防,我們首先要把三個(gè)分層,不能直接簡(jiǎn)單拿過(guò)來(lái)用,基于堆棧去預(yù)測(cè)這種情況,這樣就會(huì)更加準(zhǔn)確。這個(gè)里面會(huì)告訴你某一個(gè)類,某一個(gè)方法,底下具體是哪一行到哪一行,我們基于熱點(diǎn)覆蓋的時(shí)候經(jīng)常會(huì)提一個(gè)概念,拿到修改的行,通過(guò)修改的行去匹配,這個(gè)行的代碼就會(huì)明確的告訴你我在這行拋了,開(kāi)發(fā)去解決問(wèn)題的時(shí)候,就有經(jīng)驗(yàn)的開(kāi)發(fā),會(huì)做一件事情,永遠(yuǎn)去看0或者1的,不去看后面的,后面再拋出來(lái)的跟現(xiàn)在是完全不一樣的。既然開(kāi)發(fā)都能夠通過(guò)這個(gè)來(lái)做,我們可以轉(zhuǎn)化為一個(gè)缺陷預(yù)防,或者是判斷的技術(shù)手段,這個(gè)時(shí)候我們就可以去針對(duì)這些拋出來(lái)的Exception,再做一個(gè)排名,需要收集線上所有的日志里面,把所有日志里面無(wú)論這類也好,還是這個(gè)方法也好,只要在里面出現(xiàn)一次,我將排名就加“1”,會(huì)首先看全局的哪類哪個(gè)方法,即使行數(shù)不一樣,我要統(tǒng)計(jì)到這個(gè)方法底下服務(wù)端的排名情況。
我剛才所說(shuō)的都是為如何建立我的缺陷預(yù)防的指標(biāo)的參考依據(jù),大家可以看到這些指標(biāo),比如有多少個(gè)鏈路的調(diào)用,總共有多少函數(shù)處在這個(gè)破拋出來(lái)的這些堆棧的異常相關(guān)信息,十前面的ST作為基礎(chǔ)數(shù)據(jù),我們發(fā)現(xiàn)只是用基礎(chǔ)數(shù)據(jù)去預(yù)測(cè)Bug出現(xiàn)Stack Trace概率并不是很準(zhǔn)確,到底出現(xiàn)Bug的概率是什么樣的,我們相當(dāng)于做了一個(gè)類似于加權(quán)平均,或者統(tǒng)計(jì)學(xué)里面應(yīng)該叫取一個(gè)均值,把參數(shù)做一個(gè)綜合,舉個(gè)簡(jiǎn)單的例子,如果這個(gè)項(xiàng)目中只有兩個(gè)文件,如果拋出來(lái)一個(gè)堆棧異常,兩個(gè)類里面有多少個(gè),和我有10個(gè)文件,但是每個(gè)類里面的代碼行數(shù)對(duì)應(yīng)的行數(shù)不同,如果想全面的了解到Bug出現(xiàn)的概率,就不能簡(jiǎn)單的以其中一個(gè)指標(biāo)作為參考。做了一個(gè)組合之后,就相當(dāng)于把出現(xiàn)Bug的概率和情況做了一個(gè)更加詳細(xì)的指標(biāo)定義。我們有了這個(gè)指標(biāo)的定義,就更容易預(yù)測(cè)Bug出現(xiàn)的概率。
這是堆棧信息人工智能預(yù)測(cè)的模型,把Stack Trace和錯(cuò)誤的代碼進(jìn)行了關(guān)聯(lián),做了一個(gè)提取和模型的建立,并且自動(dòng)的把它打標(biāo),第二個(gè)階段,我已經(jīng)有了線上這么多出現(xiàn)的異常,我們新來(lái)一個(gè),能不能直接定位到某一類某一行的方法上。
濟(jì)南網(wǎng)站建設(shè)公司為什么要做這個(gè)事情?現(xiàn)在測(cè)試不是左移就是右移,對(duì)于我們來(lái)說(shuō),跟開(kāi)發(fā)合作的時(shí)候,他認(rèn)為比較理想的測(cè)試是說(shuō)不用給那么多步驟,不用那么多方法,能不能寫(xiě)一個(gè)單元測(cè)試,能不能直接告訴我問(wèn)題出在哪兒。有很多的方法是希望測(cè)試做到這樣的,并不是說(shuō)測(cè)試做不到,測(cè)試也是可以做到的,但是需要很多的工具,包括歷史數(shù)據(jù)的積累才能做到。而且有很多的情況,測(cè)試對(duì)于場(chǎng)景,包括業(yè)務(wù)了解得更多,這里只寫(xiě)了模塊的一小部分,如果把測(cè)試的這些反饋到開(kāi)發(fā)身上,就可以做到這一點(diǎn)。
這是第三代的技術(shù),會(huì)把靜態(tài)分析和動(dòng)態(tài)分析做整合,把代碼的覆蓋率,方法的覆蓋率,包括分支的覆蓋率,統(tǒng)一放到矩陣?yán)锩,在這個(gè)矩陣相對(duì)應(yīng)的,可以認(rèn)為原生的第一代是一維的,只有一個(gè)東西幫我們做判定,這個(gè)是一個(gè)二維的,到了第三代其實(shí)是做了更多維度的,但是這個(gè)維度里面既包含了靜態(tài),又把動(dòng)態(tài)的也放進(jìn)來(lái)了,同時(shí)也把覆蓋的行這些東西也都做到統(tǒng)一的PageRank Matrix里,從多個(gè)角度去看能不能預(yù)測(cè)這個(gè)Bug出現(xiàn)的概率。
最終,我們會(huì)得到一個(gè)Rank List,我們統(tǒng)一做各種排序,再去看預(yù)測(cè)Bug出現(xiàn)的概率就會(huì)比之前更加準(zhǔn)確。
還有一個(gè)是基于行為的,也是最近剛剛出現(xiàn)的用來(lái)定位Bug的,就不展開(kāi)描述了。
然后我們說(shuō)一下怎么應(yīng)用,程序頻譜是一種有效的Bug定位的方法,作為卡殼的時(shí)候,首先不能單純的說(shuō)以指標(biāo)來(lái)衡量,需要把它作為綜合的指標(biāo)去看整體的提測(cè)質(zhì)量之后,就會(huì)發(fā)現(xiàn)之間是有正相關(guān)的,或者呈線性的數(shù)據(jù)的指標(biāo),代碼寫(xiě)得越爛,出現(xiàn)的Bug概率就會(huì)越高,這是毋庸置疑的。第二,Stack Trace對(duì)于Crash的定位是很有效的,現(xiàn)在手機(jī)經(jīng)常運(yùn)行著的時(shí)候就不動(dòng)的,或者響應(yīng)比較慢,我們?nèi)ツ玫蕉褩5腻e(cuò)誤異常,上報(bào)上來(lái),這個(gè)時(shí)候?qū)τ谌ザㄎ贿@些異常是很有效的,用它來(lái)提高安卓端的響應(yīng),包括出現(xiàn)什么樣的錯(cuò)誤,這是十分有效的手段。第三,錯(cuò)誤的樣本集目前仍然不夠大,剛才說(shuō)的三種方式,還漏掉很多沒(méi)有按照規(guī)范和固定操作制度,中間有很多開(kāi)發(fā)沒(méi)有按照這個(gè)去做,有一個(gè)叫全樣本,這也是息息相關(guān)的因素。還有一個(gè),算法的多樣性,我們用靜態(tài)和動(dòng)態(tài)的調(diào)用,里面的算法也是多樣性的,目前有20多個(gè)算法,算法也決定了預(yù)測(cè)Bug出現(xiàn)概率的重要參考因素。很顯然,第一代的不如第二代的,第二代的不如第三代的,但是這些迭代的版本、方法多了之后,原來(lái)簡(jiǎn)單的算法也不適應(yīng)了,每增加一個(gè)因素,或者每增加一個(gè)參考的因子,模型也要做相對(duì)應(yīng)的處理,這樣才能預(yù)測(cè)Bug出現(xiàn)的情況。
現(xiàn)場(chǎng)視頻(下)
通過(guò)這些Bug定位的技術(shù)和方法,我們拿到最直接、最有效的方式。把它作為實(shí)地落地的情況,通過(guò)這些能夠?qū)ξ业钠髽I(yè)產(chǎn)生直接收益,一般情況下,前面分析了那么多,但是這些東西都不能落地,或者都不能提高協(xié)同的效率,推廣和應(yīng)用是很困難的,我們通過(guò)它可以做到的有這四個(gè)場(chǎng)景,能夠提高大家的效率,輔助開(kāi)發(fā)來(lái)定位Bug。阿里內(nèi)部有一個(gè)可視化的質(zhì)量分,分了四個(gè)維度,第一是圈復(fù)雜度,第二是面向?qū)ο蟮闹笜?biāo),第三個(gè)我忘記了,每個(gè)指標(biāo)里面都有因子,每個(gè)方面給一個(gè)權(quán)重,提測(cè)完代碼之后進(jìn)行開(kāi)發(fā),跟代碼的質(zhì)量分是不是有直接關(guān)聯(lián)。 指標(biāo)定義好,就可以發(fā)現(xiàn)問(wèn)題,指標(biāo)定義不好就會(huì)出笑話。
Bug的重復(fù)檢測(cè),如果我們做交叉,或者是比較大的,比如我們使用外包,免費(fèi)的從網(wǎng)上公開(kāi)共測(cè)的東西,他們提過(guò)的Bug之后,我們也可以去做去重,來(lái)決定將來(lái)判定一個(gè)重要的指標(biāo)。Bug的自動(dòng)assign,每個(gè)開(kāi)發(fā)提交的,根據(jù)這個(gè)場(chǎng)景涉及到的case,或者提交分支版本,可以將Bug和這個(gè)產(chǎn)生者直接做一個(gè)關(guān)聯(lián),出現(xiàn)了Bug就可以不需要判定是哪個(gè)開(kāi)發(fā)要做的事情,
濟(jì)南網(wǎng)頁(yè)設(shè)計(jì)可以告訴是這個(gè)東西出了問(wèn)題。然后是case的等級(jí)分類,相信大家在做第四個(gè)的時(shí)候,都應(yīng)該會(huì)做,如果不做case的等級(jí)分類,就是測(cè)試連入門(mén)都沒(méi)有做到。為什么要把它提出來(lái)?因?yàn)槲覀儼l(fā)現(xiàn)現(xiàn)在有很多的工具也可以幫我們生成單元測(cè)試的代碼,但是這些單元測(cè)試的代碼和這個(gè)之間并沒(méi)有作為強(qiáng)關(guān)聯(lián)。大家都知道“8020”法則,80%的Bug來(lái)自于20%的代碼,我們所有case也不應(yīng)該在同一個(gè)級(jí)別,可以針對(duì)這個(gè)理論,對(duì)這些20%的代碼做case等級(jí)的分類。迭代的版本越多,開(kāi)發(fā)和測(cè)試不斷在維護(hù)自己自動(dòng)化的腳本,包括單元測(cè)試的腳本,這些東西會(huì)慢慢的越積越多,這個(gè)時(shí)候之前第一個(gè)版本跑了10分鐘就能跑完,但是運(yùn)行了半年,或者是一年以上,這個(gè)項(xiàng)目沒(méi)有死,你會(huì)發(fā)現(xiàn)測(cè)試用例一直往上堆,之前跑10分鐘,半個(gè)小時(shí)就能跑完,現(xiàn)在是用空間換時(shí)間看case執(zhí)行的情況。我們除了用空間換時(shí)間之外,還可以用等級(jí)分類的方法把這些case標(biāo)志出來(lái),可以用少一些的空間,或者是少一些的時(shí)間。
轉(zhuǎn)載:Testin