楊正卉,洪 玫,郭 丹,王 瀟,劉 芳,黃小丹
(四川大學 計算機學院(軟件學院),成都 610065)
軟件測試是發現軟件缺陷的過程,是保障軟件質量的重要手段.由于被測系統的復雜性以及測試成本的限制,軟件測試自動化已經成為必然.隨著測試技術的進一步發展,測試流程的不斷規范,大量的單元測試用例生成工具涌現.這些工具以覆蓋率作為測試標準.有研究者發現,測試用例的覆蓋率是越高,捕獲代碼缺陷的可能性就越大.研究自動化單元測試用例生成工具生成的測試用例的覆蓋率和檢錯率具有重要意義,直接影響著這些測試工具是否能在業界發揮更好的作用.在國際上的一些單元測試工具競賽中[1-6],Evosuite多次獲得第一名;在2019年的競賽中,Randoop 工具和手工編寫的測試用例被作為其余工具對比的基線.因此,本文選擇Evosuite,Randoop 兩個具有代表性的工具為例,采用了2016年單元測試工具競賽中使用的數據集:Defects4J,研究自動化單元測試工具的性能,擬回答以下問題:
(1)在不同的生成時間下,Evosuite,Randoop 生成的測試用例在數量上、方法覆蓋率、分支覆蓋率等方面的表現如何?
(2)哪些因素影響了Evosuite、Randoop 工具生成的測試用例的覆蓋率?
為了分析自動化測試用例生成工具的性能,Fraser等進行了手工測試和自動化測試的對比實驗,邀請49 名參與者,從多個方面分析了手工測試用例和自動化工具Evosuite 所生成的測試用例的特定[7].Shamshiri等在Defects4J 數據集上研究了Evosuite、Randoop 工具,以及商用工具AgitarOne 的性能,回答了如何使自動化單元測試工具能夠查找到更多的缺陷的問題[8].Almasi 等使用工業界的項目——LifeCalc 對Evosuite和Randoop 工具的有效性進行了評估[9].Kotelyanskii等對基于搜索的單元測試用例工具Evosuite 的參數調優進行了研究,其研究結果為本文提供了參考[10].在這些研究中,主要關注的是工具的缺陷查找能力,但作為自動化單元測試,更重要的是其對代碼的覆蓋能力,David Schuler 確定了覆蓋率是判斷測試用例質量的一個可靠指標,覆蓋率越高,缺陷查找率可能越高[11].本文詳細分析了工具生成的測試用例對源代碼的方法和分支的覆蓋率,進一步探索了工具的特點和存在的缺陷.
為了回答上述研究問題,本文設計了比較實驗,實驗過程如圖1所示.應用自動化單元測試用例生成工具Evosuite 和Randoop,在Defects4J 數據集上,針對不同的被測軟件,分別生成測試用例.工具生成測試用例的時間分別設置為10 s,20 s,30 s,60 s,120 s,180 s,300 s.由于Evosuite 和Randoop 都是隨機生成測試用例,每次產生的結果有一定隨機性.因此,在實驗設計上,同一生成時間重復運行Evosuite 和Randoop 3 次,并以3 次運行結果的平均值作為實驗分析數據.
Randoop[12]是基于反饋的面向對象單元測試的隨機測試用例生成工具,對于被測類,Randoop 創建一系列方法調用和構造函數,依次創建和更改對象狀態.執行所有創建的序列,并用執行的生成來創建捕獲系統行為的斷言.Randoop 的輸入是一組要測試的Java 類,一個時間限制和一組可選的規范檢查器,生成是一組對應的JUnit 測試用例.Randoop 的隨機種子在缺省情況下為0,對于相同被測類,每次運行時需更改隨機種子的值.另外,Randoop 工具在生成過程中易產生片狀測試,導致Randoop 工具無法生成更多的測試用例.為了解決該問題,需將flaky-test-behavior 參數設置為OUTPUT.
Evosuite[13]是基于搜索的單元測試用例生成工具,該工具被集成于Eclipse,IntelliJ,Maven 等環境下,可以生成Java 類的JUnit 測試用例.該工具采用遺傳算法,從一組隨機測試用例開始,迭代地應用選擇、突變和交叉等搜索操作符來進化測試用例,進化由基于覆蓋標準的適應度函數指導,一旦搜索結束,測試用例就會被最小化,并添加測試斷言.在本實驗中,除了禁止Evosuite 使用單獨的類加載器,更改生成時間以外,其余均采用默認配置.

圖1 實驗過程設計
實驗被測軟件采用Defects4j 數據集[14]中的5 個開源項目的多個版本.這些項目版本中有357 個真實缺陷:JFreeChart (26 個缺陷)、Google Closure compiler(133 個缺陷)、Apache Commons Lang (65 個缺陷)、Apache Commons Math (106 個缺陷) 和Joda Time(27 個缺陷).Apache Commons Lang 是處理Java 基本類方法的工具類包,彌補了Java.Lang API 基本處理方法上的不足;Apache Commons Math 是輕量級自容器的數學和統計計算方法類包,包含大多數常用的數值算法,外部依賴較少.JFreeChart 是為Applications,Applets,Servlets 以及JSP 等使用所設計的,可生成多種圖表,產生PNG 和JPEG 格式的生成.對于項目版本的每一個缺陷,Defects4j 提供了:(1)該缺陷對應的缺陷版本和已修復版本;(2)開發人員手工編寫的可以揭露缺陷的測試用例;(3)缺陷類的列表.
本實驗選擇JFreeChart,Apache Commons Lang,Apache Commons Math 作為被測軟件,既滿足了多樣性的需求,也保證了所選擇的軟件的代表性.實驗將3 個軟件每個版本中已修復缺陷的類作為被測類.當某個類在缺陷類列表中多次出現時,使用該類第一次出現缺陷時對應的已修復版本.本實驗共篩選出71 個被測類.
本實驗在Windows 64 位操作系統下采用SUN Java 8 SE,Eclipse Europa IDE 編譯被測項目.在JUnit 4 框架下運行測試用例,使用EclEmma 插件計算測試用例覆蓋率.編程自動統計覆蓋率結果,并采用Excel工具對結果進行分析.
(1)編譯被測類
Apache Commons Lang 和Apache Commons Math是Maven 構建的項目,編譯該項目時,需確保Eclipse中已配置Maven 環境.
(2)測試用例生成
在命令行中使用Evosuite-1.0.5.jar 和Randoop-4.0.4 生成測試用例.在生成測試用例時,生成時間設為:10 s,20 s,30 s,60 s,120 s,180 s,300 s.Evosuite 和Randoop 工具分別在每個生成時間下運行3 次.每次運行時,Evosuite 參數保持不變.Randoop 隨機種子分別設置為123,234,456,其余參數保持不變.
(3)測試用例執行與覆蓋率計算
測試用例在Eclipse 中使用JUnit 4 框架分別執行.測試覆蓋率由eclEMMA 插件統計,結果在測試用例執行結束后得到.Randoop 生成的測試用例分為回歸測試用例和揭錯測試用例,這兩種測試用例都應被執行.考慮測試的穩定性,若測試用例不能執行或執行中斷,則丟棄該測試用例.
本實驗從以下幾個角度統計覆蓋率結果:
① 以類為單位,統計Evosuite 和Randoop 測試用例對被測類的方法覆蓋率和分支覆蓋率.
② 以方法為單位,統計Evosuite 和Randoop 測試用例在每個方法中的分支覆蓋率.
(4)實驗結果處理
實驗中存在無法生成測試用例或測試用例無法正常運行的情況,比如個別類或者方法不存在分支,此時默認分支覆蓋率為100%.
(1) Evosuite 和Randoop 工具生成的測試用例數量.
圖2展示了Evosuite 和Randoop 工具生成的測試用例數量與生成時間的關系.無論生成時間如何設置,Evosuite 工具生成的測試用例數量相對穩定.Randoop工具生成的測試用例數量隨著生成時間的增加而增加.當生成時間為60 s 時,明顯看出Randoop 測試用例數量多于Evosuite.圖3展示了在不同項目中 Evosuite和Randoop 生成的測試用例數量對比,當生成時間為10 s,20 s 時,Randoop 生成的測試用例數量依舊高于Evosuite.因此在所有生成時間設置下,Randoop 工具生成的測試用例數量都多于Evosuite.因為Evosuite 工具會對生成的測試用例進行最小化操作,保留符合覆蓋率標準的測試用例.而Randoop 工具則輸出生成的所有測試用例.
(2) Evosuite 和Randoop 工具生成的測試用例的方法覆蓋率.
圖4展示了Evosuite 和Randoop 工具生成測試用例對被測類的方法覆蓋率與分支覆蓋率.在3 個被測軟件中,Evosuite 測試用例的方法覆蓋率隨著生成時間的增加而增加.Randoop 測試用例的方法覆蓋率在生成時間為10~120 s 時存在遞增現象,并在120 s 時達到最大值.在Chart 項目中,當生成時間為300 s 時,Randoop 測試用例的方法覆蓋率有所降低;在Lang 項目上,當生成時間超過120 s 后,Randoop 測試用例的方法覆蓋率保持平穩;對于Math 項目,當生成時間超過120 s 后,Randoop 測試用例的方法覆蓋率逐漸降低.當且僅當生成時間為10 s 時,對于Chart 項目,Randoop測試用例的方法覆蓋率高于Evosuite.當生成時間達到20 s 后,無論在哪個項目上,Evosuite 測試用例的方法覆蓋率都高于Randoop.總體來看,Evosuite 測試用例的方法覆蓋率高于Randoop.

圖2 Evosuite 和Randoop 工具生成的測試用例數量與生成時間的關系

圖3 在不同的項目中,Evosuite 和Randoop 生成的測試用例數量對比

圖4 Evosuite 和Randoop 測試用例對被測類的方法覆蓋率與分支覆蓋率比較
(3) EvosuiteRandoop 工具生成的測試用對被測類的分支覆蓋率.
在Chart 和Math 項目中,Evosuite 工具生成的測試用例對被測類的分支覆蓋率隨著生成時間的增加逐漸上升.在Lang 項目中,當生成時間為30 s 時,Evosuite工具生成的測試用例的分支覆蓋率較生成時間比20 s 時略有降低.但在其余生成時間上,Evosuite 工具生成的測試用例的分支覆蓋率依舊隨著生成時間的增加而增加.當生成時間為10~120 s 時,Randoop 工具生成的測試用例的分支覆蓋率隨著生成時間的增加而增加.當生成時間達到120 s 后,在Chart 和Lang 項目上,Randoop 工具生成的測試用例的分支覆蓋率達到最優值,并保持穩定.在Math 項目上,Randoop 工具生成的測試用例的分支覆蓋率逐漸下降.當且僅當生成時間為10 s 時,在Chart 項目上Randoop 工具生成的測試用例分支覆蓋率與Evosuite 工具生成的測試用例分支覆蓋率相當,當生成時間達到20 s 后,Evosuite 測試用例的分支覆蓋率高于Randoop 測試用例.整體來看,Evosuite 對被測類的分支覆蓋率同樣優于Randoop.
(4) 影響Evosuite 對被測類的分支覆蓋率高于Randoop 的因素.
從上述結論可知,Evosuite 對被測類方法覆蓋率高于Randoop.原因之一是Randoop 工具的隨機性,不能覆蓋部分方法,就不能覆蓋到方法中的分支,而Evosuite可實現更多的方法覆蓋,可覆蓋到方法中的分支.圖5展示了同時被Evosuite 和Randoop 測試用例覆蓋的方法的分支覆蓋率.從圖5可見,當Evosuite 和Randoop同時覆蓋某個方法時,Evosuite 測試用例對該方法的分支覆蓋程度高于Randoop.因此,Evosuite 對被測類分支覆蓋率高于Randoop 不僅因為Evosuite 覆蓋了更多的方法,還因為Evosuite 測試用例較Randoop 可以覆蓋到方法中的更多分支.

圖5 同時被Evosuite 和Randoop 測試用例覆蓋的方法的分支覆蓋率
通過分析被測軟件,發現軟件版本中存在抽象類,而Randoop 工具在執行時,會忽略這些抽象類以及接口.所以,即使抽象類中存在著具體的方法(例如:Math_12,Lang_15 等),Randoop 工具也沒有對這些方法生成測試用例.可以認定,被測軟件中存在的抽象類是影響Randoop 工具生成的測試用例覆蓋率的因素之一.在Randoop 工具改進中,建議考慮對抽象類的處理.
圖6展示了Randoop 工具所生成的一個測試用例的樣例.在執行該用例時,發現深色標注的代碼未執行,從而未覆蓋該代碼所調用的被測方法.從這一測試用例中可以看出,Randoop 在生成測試用例時,隨機模擬被測方法中所需要的參數來調用方法.當Randoop 工具不確定測試代碼是否能成功執行時,將會以trycatch 語句包裹該代碼.實驗證明,若測試用例中某些語句被try-catch 包裹,則該語句基本無法正常執行,從而導致無法覆蓋其中被調用的被測方法.因為Randoop不能成功模擬調用該被測方法所需要的參數.Randoop的設計者也提到:目前Randoop 對于參數的選擇是隨機的.因此,有效模擬被測方法所需參數是影響Randoop覆蓋率的因素之一.當Randoop 需要模擬某些參數來調用方法時,可以依照上下文關系獲取該參數的值,而不是重新定義隨機值[15].
在圖4中,當生成時間足夠長時,Evosuite 測試用例的方法覆蓋率達到90%以上,方法覆蓋程度并不是影響該工具分支覆蓋率的重要因素.圖7展示了 Evosuite工具對某被測模塊的覆蓋情況的樣例,Evosuite 工具所生成的測試用例雖然覆蓋了該方法,但當方法中出現分支時,Evosuite 無法覆蓋該分支中所包含的語句,該分支的運行依賴于其他方法的執行結果.因此,Evosuite工具對被測方法相關的依賴方法結果的構造是影響分支覆蓋的因素之一.

圖6 Randoop 工具生成的一個測試用例樣例

圖7 Evosuite 工具對某被測模塊的覆蓋情況的樣例
內部有效性,Evosuite 和Randoop 工具都具有一定的隨機性,若要評估工具測試用例的覆蓋率,應生成盡可能多的測試用例.本實驗在多個項目、多個版本、多個生成時間上分別生成3 組測試用例,實驗數據具有一定的統計意義.在實驗過程中,Randoop 工具每次運行所生成的測試用例的方法覆蓋率和分支覆蓋率波動較小;Evosuite 覆蓋率波動情況較Randoop 更大,若以工具的最優值作為衡量標準,Evosuite 測試用例的覆蓋率結果將更優.但就Evosuite 運行情況來看,采用最小值、最大值、平均值完整體現工具的真實情況,衡量工具覆蓋率更為合理.
外部有效性,從實驗結果來看,Evosuite 工具生成的測試用例的方法覆蓋率和分支覆蓋率隨著生成時間的增加而增加.Randoop 工具在生成時間達到120 s 后,對于不同的項目方法覆蓋率和分支覆蓋率具有不同的特征.但Lang 和Math 項目是Apache Commons 中的基礎工具包,使用頻率較高,Chart 項目充分體現了繪圖程序的基本特征,因此,本實驗選擇的被測項目既滿足了多樣性的需求,也保證了所選擇的軟件的代表性.
本實驗通過對Evosuite 和Randoop 工具所生成的測試用例的覆蓋率分析發現:① 即使生成時間低至10 s,Radnoop 所生成的測試用例數量依舊高于Evosuite.并且當生成時間到達20 s 后,單元測試工具Evosuite生成的測試用例的方法覆蓋率和分支覆蓋率覆蓋率高于Randoop.② 影響Randoop 工具覆蓋率的原因包含如何處理抽象類以及如何模擬被測方法所需要的參數等.Evosuite 工具提高覆蓋率的關鍵問題是如何構造與被測方法相關的依賴方法結果,以保證能夠覆蓋更多的分支.在未來的工作中,可以研究是否可以采用符號化執行與隨機測試相結合的方法提高Randoop 工具的參數模擬能力.