李晨章,高建華
(上海師范大學(xué) 計算機科學(xué)與技術(shù)系,上海 200234)
JavaScript是一種解釋型的腳本語言,它基于對象和事件驅(qū)動,具有相對安全性等特點[1].JavaScript運行在客戶端,它不與某一單獨的瀏覽器綁定但可以嵌入到任何的Web頁面中,節(jié)省了Web服務(wù)器的請求時間和帶寬,運行的結(jié)果和處理相對比較快.然而在實際編寫JavaScript代碼的過程中,一個細微的錯誤有可能導(dǎo)致一個HTML頁面的無法正常顯示乃至整個Web應(yīng)用的癱瘓.
JQuery作為一個繼Prototype后快速、簡潔的JavaScript框架[2].設(shè)計之初就按照Write Less,Do More的思想,通過編寫更少的代碼實現(xiàn)更多功能.JQuery封裝了JavaScript常用的功能代碼,升級優(yōu)化了Html處理事件、交互等方面.
由于jQuery是一種動態(tài)語言并且有著強大的拓展性,致使它更容易出錯.同時又因jQuery對頁面元素或者樣式的操作是被封裝的,導(dǎo)致在自動化測試過程中很難判斷執(zhí)行結(jié)果,所以現(xiàn)階段jQuery沒有辦法被很好的測試.
Y.Jia和M.Haram[3]提出一種變異測試的方法,并廣泛應(yīng)用在各個語言的軟件上.雖然變異測試已經(jīng)被證明了在提高測試用例的質(zhì)量上是有效的,但是測試成本也隨之增加,這取決于生成的變異體的數(shù)量和產(chǎn)生等價變異體的百分比.
為了降低變異測試的成本,國際上許多學(xué)者提出了幾種可行的方法.A.J.Offutt和R.H.Untch[4]把這些方法分為了三類,即減少變異、更好的算法以及更快生成和運行.將這三類辦法應(yīng)用在變異測試分析中,可以大幅降低變異測試的成本,剔除測試過程中的不必要的開銷.
A.P.Mathur[5]提出了selective mutation 的方法.A.J.Offutt[6]等人又基于此方法提出了N-Selective方法,接著又通過10個程序作為測試數(shù)據(jù)進行了大量的實驗,對結(jié)果進行分析后得出可以通過選擇性地應(yīng)用有效的變異算子來減少變異體的數(shù)量,并將多余的變異體排除在測試的過程之外.
S.Mirshokraie[7]等人提出了一種被稱為FunctionRank的算法,即在JavaScript變異測試中選擇更有效的分支和變量進行變異,同時提出了針對jQuery的三類變異算子,但是這三類變異算子并不能完全覆蓋jQuery中所有問題.
U.Praphamontripong和A.J.Offutt[8]提出了Web應(yīng)用中特定的變異算子,并在這些變異算子中尋找冗余算子,卻未細化到針對jQuery的變異算子.
J Zhang[9]等人將機器學(xué)習(xí)運用在變異測試中來預(yù)測執(zhí)行結(jié)果,但由于所需樣本數(shù)量巨大、正確率不到70%和未普及到j(luò)Query中,使該方法暫時不適用于jQuery變異測試.
本文基于變異測試技術(shù),設(shè)計了13個針對jQuery的變異算子,并通過實驗對變異算子中冗余進行了研究.在時間或計算資源有限的情況下,選擇有效變的異算子將減少對jQuery變異分析時所需的成本.
第2節(jié)給出了相關(guān)的一些術(shù)語,第3節(jié)描述了變異測試的基本流程,第4節(jié)列出了針對于JQuery的特定的變異算子,第5節(jié)展示了通過實驗獲得的數(shù)據(jù),并且討論了結(jié)果.
變異測試的概念最早由R.A.DeMillo等人[10]與R.G.Hamlet[11]提出,且變異測試已應(yīng)用到了各種語言和軟件測試的各個層次上,例如C語言、Java語言、Android軟件[12]等.
變異是一種變更程序的行為.用P表示被測原始程序,M表示變更后得到的程序,那么M稱為P的變異體.變異一個程序意味對該程序進行變更以達到測試評價的目的,是一種用于評價一個測試集針對特定程序優(yōu)良程度的手段.
變異算子一般在語法條件允許情況下,僅對被測程序作細微改動.設(shè)計變異算子主要目的在于模擬程序員可能犯的簡單錯誤.通常為了提高代碼覆蓋率和域覆蓋率,測試人員需要設(shè)計更有效算子.表1展示了一個典型變異算子.
表1 一個典型的變異算子
Table 1 A typical mutation operator
原程序P變異體M……if ( a+b>c )if ( a-b>c ) return true; return true;……
定義1.等價變異體:給定程序P生成的變異體M,若對于所有可能的測試輸入t,P(t)=M(t)都成立,就認為變異體M等價于原程序P,也稱變異體M是等價變異體,因為它雖然在語法上存在著差異但是與原程序P的功能保持一致.表2給出了一個典型的等價變異體.
表2 一個典型的等價變異體
Table 2 A typical equivalent mutant
原程序P變異體M…… for (int i=0;i<10;i++) {for(int i=0;i!=10;i++){ sum + = a[i]; sum + = a[i];}}……
定義2.可殺死變異體:給定程序P生成的變異體M,若存在測試用例t(t∈T),M上的執(zhí)行結(jié)果不同于原程序P,則稱變異體M相對于測試用例集T是可被殺死變異體,也就是說該變異體是可以被檢測的.
定義3.可存活變異體:若不存在任何測試用例t(t∈T),在變異體M上的執(zhí)行結(jié)果不同于原程序P,則稱變異體M相對于測試用例集T是可存活變異體.可存活變異體中由一部分可通過設(shè)計新的測試用例來轉(zhuǎn)化為可殺死變異體,其余的可存活變異體則很可能是等價變異體.
變異測試的一般生成與測試方法是:
1)將變異算子O應(yīng)用在程序P的源代碼上以生成變異體M;
2)排除等價變異體;
3)在剩余的非等價變異體上執(zhí)行測試用例集,判斷是否檢測出所有的變異體.
變異測試基本流程如圖1所示.
圖1 變異測試基本流程Fig.1 Basic process of mutation testing analysis
變異測試分析通過變異得分MS(Mutation Score)來評估整個測試用例集T的缺陷檢測能力,變異得分MS(T)可通過公式(1)計算獲得:
(1)
其中|D|是可被測試用例集T殺死的變異體的數(shù)量,|E|是生成等價變異體的數(shù)量,|M|是生成變異體的總數(shù)量.MS(T)的取值范圍介于0到1之間.MS(T)的取值越高,代表測試用例集的實際缺陷檢測能力越強.變異測試分析的最終目標(biāo)是希望MS(T)的取值達到或者更接近于1,也就是測試用例集可以檢測出所有的非等價變異體.
使用變異得分進行測試用例評估的過程中存在對測試用例的充分性沒有任何貢獻的冗余變異體,它們會導(dǎo)致變異得分變得不那么準(zhǔn)確.例如,一些變異體幾乎能被任何其他的測試用例殺死,如果不考慮這些變異體,并不會影響如何選擇測試用例,但是會導(dǎo)致不同的變異得分.總之,冗余變異體不僅會影響整個變異測試的計算和分析開銷外,也會影響變異得分.所以如果找到這些生成冗余變異體的變異算子,在變異算子應(yīng)用于原程序前就將冗余變異算子排除在變異算子集合外,能大幅提高效率.
計算變異體冗余的公式如下:
(2)
公式(2)中mj是第j個變異算子被其它變異算子對應(yīng)的測試用例集殺死的數(shù)量,Mj是第j個變異算子總共生成的變異體的數(shù)量.
本節(jié)針對JavaScript中的JQuery代碼進行了變異測試分析.變異測試的有效性主要取決于產(chǎn)生變異體的變異算子,在設(shè)計變異算子時應(yīng)深入了解jQuery中潛在的問題,總結(jié)一些常見的錯誤.
S.Mirshokraie等人[7]首次提出了針對JQuery的三類變異算子.這三類變異算子具體如下:
1)交換選擇器中的{#}和{.}符號
{#}在JQuery選擇器中表示指定id的元素.id引用HTML元素id屬性,id屬性在HTML中必須是唯一的.若在同一頁面中出現(xiàn)多個id名稱相同的元素,會導(dǎo)致選擇器失效,不能選中對應(yīng)元素.在默認瀏覽器中,如果使用數(shù)字開頭的id名稱可能會導(dǎo)致問題的發(fā)生.
{.}在JQuery選擇器中表示指定class的元素.class引用HTML元素class屬性.與id選擇器不同,由于存在擁有同一class屬性的元素,class選擇器通常可以選擇多個元素.
將{#}選擇器和{.}選擇器互換會使選擇域發(fā)生變化,導(dǎo)致選擇器無法選中目標(biāo)或選擇器失效.
2)移除jQuery對象前的{$}符號
通常jQuery對象用var開頭定義,{$}符號是jQuery對象的標(biāo)識.在生成或者選擇已有的元素時,jQuery對象會返回一個類似數(shù)組形式的元素的集合.
由于jQuery對象實例和DOM對象實例擁有不同的方法和屬性,它們是兩種完全不同的對象.若將{$}符號移除或替換可能會導(dǎo)致web應(yīng)用報錯或選擇器錯誤.
3)改變屬性/類/元素的名字
addClass方法可以向被選中的jQuery對象的每一個元素上添加指定的css類名,可以是一個或多個.
removeClass方法用來移除被選中的 jQuery對象的一個元素上指定的css類名,可以是一個或多個.
removeAttr方法用來移除被選中的jQuery對象的每一個元素上指定的屬性名.
remove方法在不存在參數(shù)時會刪除所選元素及其子元素,在存在參數(shù)時會根據(jù)參數(shù)所涉及的范圍進行相對應(yīng)刪除.
detach方法和remove方法類似,但detach方法會保留對象中的匹配元素,可以在將來再使用這些被保留的元素.
prop方法用于設(shè)置或者返回當(dāng)前jQuery對象所匹配到的元素的屬性值.
css方法用于設(shè)置或者返回被選元素的一個或者多個樣式屬性.
其中第1類和第2類變異算子用于分析選擇器錯誤,第3類變異算子主要針對jQuery對象的方法和屬性錯誤.
上述三類jQuery變異測試中的變異算子在實際使用中并不能很好的覆蓋一些真實的錯誤類型,例如parent方法和parents方法的錯誤使用、text方法和html方法的錯誤使用等.如果用上述的三類變異算子,并不會對所有語句進行變異操作分析,因此無法模擬這類語句上的錯誤.
本文在已提出的jQuery的錯誤基礎(chǔ)上,對第3類原有變異算子的基礎(chǔ)上進行了補充,提出了三類新的變異算子,即第4、5和6類變異算子.在第3類的基礎(chǔ)上補充添加了以下幾個方法:parent,parents,children,find,text,html,append,after,prepend,before,empty.parent方法用來查找并返回被選元素的直接父元素.
表3 13個變異算子
Table 3 Thirteen mutation operators
變異算子原始代碼變異后代碼Selector Sign Problem(SIP) 選擇器符號錯誤交換選擇器中的{#}和{.}符號O1O2$(′#id′)$(′.class′)$(′.id′)$(′#class′)移除符號{.}或者符號{#} O3O4$(′#id′)$(′.class′)$(′id′)$(′class′)移除jQuery對象前的{$}符號O5var $me = $(′#id′);var me = $(′#id′);this替換 {$()}中的內(nèi)容 O6$(′#id′) or $(′.class′)$(this)String Joint Problem (SJP) 字符串拼接錯誤改變jQuery對象拼接字符串中{′}和{″} O7$(′#id′+value+ ′num′).val();$(″#id′+value+ ″num′).val();Conception Confusable Problem (CCP) 概念易混錯誤 O8O9parent()parents()parents()parent()O10O11remove()detach()detach()remove()O12O13text()html()html()text()
parents方法用來查找并返回被選元素的祖先元素的集合(不包括根元素).
children方法和find方法類似于parent方法和parents方法,區(qū)別在于查找返回的是直接子元素和后代元素.
text方法用來設(shè)置或者返回被選元素的文本內(nèi)容.
html方法是用來設(shè)置或者返回被選元素的內(nèi)容.
append方法是用來在被選元素結(jié)尾處插入內(nèi)容.
after方法是用來在被選元素之后插入內(nèi)容
prepend方法和before方法類似,區(qū)別在于在被選元素開頭和之前插入內(nèi)容.
4)this替換{$()}中的內(nèi)容
this在選擇器中是指已經(jīng)綁定了當(dāng)前操作的標(biāo)簽,$(this)則表示為當(dāng)前對象.
將{$()}中的內(nèi)容用this替換后會引用錯誤的對象,可能會引起選擇器的失效.
5)改變jQuery對象拼接字符串中{′}和{″}
jQuery對象拼接字符串中的{′}和{″}進行交換,如果{′}{″}不成對出現(xiàn)或是交錯嵌套使用,均會導(dǎo)致web應(yīng)用報錯或選擇器錯誤.
6)移除符號{.}或者符號{#}
其中,第4類和第6類均屬于選擇器符號錯誤,第5類屬于字符串拼接錯誤.
因為針對jQuery方法和屬性錯誤的變異算子過于繁多,所以在最終選擇變異算子時,基于熟練程序員假設(shè)[6],更關(guān)注那些功能類似且易于混淆的變異算子,它們更能模擬熟練程序員的實際編程問題.最終在針對jQuery方法和屬性錯誤的變異算子中選擇了6個,即parent,parents,remove,detach,text,html.
針對jQuery的冗余問題,歸納后提出了表3中的13 個jQuery特有的變異算子,并進行了變異測試和分析.其中,選擇器符號錯誤對應(yīng)第1類、第2類、第4類和第6類變異算子,字符串拼接錯誤對應(yīng)第5類變異算子,概念易混錯誤對應(yīng)第3類變異算子.
本文在實驗過程中主要尋求以下幾個問題的解答:
Q1:哪幾個jQuery變異算子可能是冗余的?
Q2:是否存在質(zhì)量較高的jQuery變異算子?
Q3:哪幾個jQuery變異算子生成的變異體很少被其它變異體的測試用例殺死?
由于現(xiàn)階段沒有適合jQuery的變異生成工具,所以本文使用Python編寫代碼.本文采用Python中提供的Selenium第三方包,通過使用Python和Selenium的環(huán)境,可以做到打開頁面,運行測試用例,并提示錯誤原因.Selenium是一個用于Web應(yīng)用程序測試的工具,可以按腳本代碼依次完成輸入、打開、點擊等操作,從用戶角度測試了web應(yīng)用.
本文選取某品牌官方商城中jQuery代碼居多的Try,Search,Cart,Confirm四個功能頁面中相應(yīng)的代碼,對應(yīng)的具體功能是申請試用、搜索商品、購物車、付款.以付款功能頁面中為例,jQuery被用來操作商品數(shù)量的增減或刪除、地址的選擇(異步)、優(yōu)惠券的使用、支付方式的選擇等.四個項目(功能頁面)中jQuery代碼總長度共計348行.
首先將所有的13個變異算子都應(yīng)用在每一個項目上,生成對應(yīng)算子的變異體,并依次對每個變異算子所生成的變異體文件分類保存.
通過將第j個變異算子應(yīng)用在第i個項目上得到的變異體稱為變異體Mij.在設(shè)計測試用例方面,針對每個不同的變異算子和每個不同的項目設(shè)計了與之一一對應(yīng)的測試用例,以殺死所有非等價變異體.
四個項目總共生成了179個變異體.討論并區(qū)分了4個實驗對象中的等價變異體,并且設(shè)計了129個測試用例,具體情況見表4.
表4 各項目代碼及變異情況
Table 4 Project code and mutation information
項目編號項目名具體功能LOC變異體等價變異體殺死的變異體測試用例S1Try試用申請795024839S2Search搜索商品1797957453S3Cart購物車573203223S4Confirm付款331801814Total3481797172129
表5 變異體情況
Table 5 Mutants information
編號LOCO1O2O3O4O5O6O7O8O9O10O11O12O13TotalS1793939212121301248S2179513513416441223274S357353528210010232S433323214020100018Total348142914299407926346172
從表4可知,S1共獲得50個變異體,S2共獲得79個變異體,S3共獲得32個變異體,而S4共獲得了18個變異體.其中等價變異體的總數(shù)量為7個,占四個項目所生成的變異體總和的3.91%,非等價變異體的總數(shù)為172個.而等價變異體中個3個為O1類型,2個為O6類型,1個為O10類型,1個為O13類型.
在分析O1變異算子產(chǎn)生的2個等價變異體后發(fā)現(xiàn),因為存在某些節(jié)點的id名稱和class名稱一致,有且只有一對的情況下導(dǎo)致了等價變異體的生成.這通常是程序員在編寫代碼時沒有規(guī)范命名,使變異前通過id選取的選擇器和變異后通過class選取的選擇器選中的對象一致,對該選擇器的行為和選中對象沒有任何影響.而通過O6算子生成的2個等價變異體是因為節(jié)點選擇器在方法內(nèi)部且變異前選擇器指向的也是該方法所選中的節(jié)點,將選擇器中的內(nèi)容在變異為this之后,選擇器選取的對象域未發(fā)生改變.剩余的2個通過O10和O13算子所生成的變異體,在變異前后,功能上未發(fā)生改變,所以在變異后對源代碼沒有影響.
表5給出了依靠前文中提出的變異算子在每個項目上產(chǎn)生的變異體的具體數(shù)量,以及由變異算子生成的變異體的總數(shù).因為O1算子和O3算子、O2算子和O4算子,這兩對算子均通過各自相同的選擇器生成,所以在沒有等價變異體的情況下,數(shù)量上是相等的.
表6給出了被殺死變異體情況的數(shù)據(jù),其中“測試用例數(shù)”所在的列記錄了殺死每種算子的所有非等價變異體所需要的測試用例的數(shù)量.為了獲得對于每一個項目的這些數(shù)值,對所有的變異體執(zhí)行了每一個測試用例集,并且記錄了對于每種算子的被殺死的變異體的數(shù)量.表6同時給出變異得分(MS)列,該得分通過公式(1)計算得出,其值越高代表該測試用例檢測能力越強,但由于可能包涵了冗余變異體,這個得分就變得不那么準(zhǔn)確.
表6 變異體被殺死情況
Table 6 Killed mutants information
測試用例集測試用例數(shù)被殺死的變異體O1O2O3O4O5O6O7O8O9O10O11O12O13TotalMST_O11114314191255000000810.47T_O22322912833340000001000.58T_O310131714211112000000790.46T_O42222611293191001000920.53T_O593423970200000300.17T_O6278168152403000000920.53T_O761313027000010180.10T_O820100000912101150.09T_O92000000032001060.03T_O1060000003006113140.08T_O1130001000105300100.06T_O1232000000200042100.06T_O1350000200103116140.08
由表6可知,需要11個測試用例來殺死所有通過O1算子產(chǎn)生的變異體,這個測試用例集叫做T_O1.“被殺死的變異體”所在的15列中的前13列給出了被測試用例集殺死的變異體的數(shù)量,每個測試用例集能殺死的變異體的數(shù)量總和等于最后一列數(shù)值.測試用例集T_O1同時能殺死O1算子產(chǎn)生的變異體和其他6個算子產(chǎn)生的變異體.對于某些變異算子而言,被殺死的變異體的數(shù)量遠遠小于所用的測試用例的數(shù)量,這是因為一部分測試用例可以殺死多個變異體,也就意味著對角線上的數(shù)值至少和左邊測試用例的數(shù)值一樣大,甚至大于左邊測試用例數(shù)值.
本文考慮了每一組測試用例如何能更有效的殺死各種變異體.針對某一變異體所設(shè)計的測試用例集如果能殺死其他變異體的比例越高,那么越有可能存在冗余的變異算子.
表7 jQuery變異算子冗余
Table 7 Redundancy of jQuery mutation operators
O1O2O3O4O5O6O7O8O9O10O11O12O13T_O11.000.101.000.660.110.630.710.000.000.000.000.000.00T_O20.141.000.070.970.330.830.570.000.000.000.000.000.00T_O30.930.591.000.720.110.280.290.000.000.000.000.000.00T_O40.140.900.791.000.330.480.140.000.000.170.000.000.00T_O50.210.140.140.101.000.180.000.220.000.000.000.000.00T_O60.570.550.570.520.221.000.430.000.000.000.000.000.00T_O70.070.100.070.100.000.051.000.000.000.000.000.250.00T_O80.000.030.000.000.000.000.001.000.500.330.330.000.17T_O90.000.000.000.000.000.000.000.331.000.000.000.250.00T_O100.000.000.000.000.000.000.430.000.001.000.330.250.50T_O110.000.000.000.030.000.000.000.110.000.831.000.000.00T_O120.140.000.000.000.000.000.000.220.000.000.001.000.33T_O130.000.000.000.000.220.000.000.110.000.500.330.251.00
表7列出了每個變異算子基于公式(2)所獲得的總體冗余.每個變異算子的整體冗余是基于該變異算子生成的變異體在某個測試用例集下所被殺死的變異體的數(shù)量除以該變異算子生成的變異體的總數(shù)量.
根據(jù)實驗結(jié)果,可逐一回答前文所提的Q1、Q2、Q3問題.
Q1:O1算子的測試用例集能殺死100%的O3算子生成的變異體,O2算子的測試用例集能殺死97%的O4算子生成的變異體.對于O3算子的測試用例集而言,它能殺死93%的O1算子生成的變異體同時也能殺死72%的O4算子生成的變異體.O4算子的測試用例集能殺死90%的O2算子生成的變異體,同樣也能殺死79%的O3算子生成的變異體.這表明在O1、O2、O3、O4算子中存在著冗余.
為了確定哪些變異算子可以移除,本文考慮了在殺死其他算子生成的變異體方面的有效性.值得注意的是,由于O3和O4算子的測試用例集在殺死O6和O7生成的變異體的個數(shù)上明顯少于O1和O2算子的測試用例集殺死O6和O7算子生成的變異體的個數(shù).因此O1和O2算子生成的變異體更強.所以,可以將O3和O4算子排除在外.
另一個有可能會產(chǎn)生冗余的變異算子是O11,因為O11對應(yīng)的測試用例集殺死了83%的O10算子生成的變異體.雖然從數(shù)值上來看還是相對較小,且O11的測試用例集不能有效的殺死其他變異體,但是也可能會引起冗余.
Q2:在判斷是否存在質(zhì)量較高的變異算子時,本文采用的方法是將可能引起冗余的變異算子排除,再根據(jù)變異得分來衡量.故在排除O3、O4后對MS進行排序,排名前三的變異算子為O1、O2和O6.其中O2和O6的MS都高于0.5,所以可以認為O2和O6是質(zhì)量較高的變異算子,因為針對他們所設(shè)計的測試用例集能殺死超過50%的變異體.
Q3:由表7可明顯發(fā)現(xiàn),O5、O7和O9 算子生成的變異體很少被其它變異體的測試用例殺死.
變異分析是一種設(shè)計、評估和提高測試質(zhì)量的有效方法.變異分析主要受計算成本等制約,而成本的一個主要因素是變異體生成的數(shù)量.本文對jQuery中變異算子的冗余性進行了實證研究.根據(jù)研究,從先前定義的變異算子中發(fā)現(xiàn)對jQuery的測試幾乎沒有貢獻的變異算子,提出了13個針對于jQuery 的變異算子,隨后在四個項目中應(yīng)用這些變異算子,生成了變異體,并對所有生成的變異體進行了的測試.對每個變異算子生成的變異體進行了測試并記錄相關(guān)數(shù)據(jù).根據(jù)這些數(shù)據(jù),計算出了之前提出的jQuery中13個變異算子的冗余.結(jié)果表明,兩個變異算子生成的變異體在很大程度上是冗余的,并且可以在故障檢測最小損失時去除,它們是O3算子和O4算子.實驗結(jié)果同時表明,O1算子和O2算子對應(yīng)的測試用例集可以有效的殺死其他幾種變異算子生成的變異體,這表明了O2和O6算子生成的變異體可能特別強.本文研究后發(fā)現(xiàn)O11算子也存在冗余的可能性,因為這個變異算子至少創(chuàng)建了許多冗余.在將來,計劃擴展更多的變異算子來測試jQuery下的AJAX以及擴大樣本數(shù)量、采用機器學(xué)習(xí)等.