EvoSuite: 面向對象軟件的自動化測試套件生成

EvoSuite: 面向對象軟件的自動化測試套件生成

摘要

為了發現軟件中的缺陷,需要系統地執行軟件的測試用例以及需要在運行這些測試用例時評估觀察到的行為正確性的結果參照物。本文介紹了 EvoSuite,它是一種工具,該工具自動使用 Java 代碼編寫的類的斷言生成測試用例。為了實現這一目標,EvoSuite 應用了一種新穎的混合方法,該方法可以生成並優化整個測試套件以滿足覆蓋標準。對於產生的測試套件,EvoSuite 通過添加少量有效的斷言來簡要總結當前行為從而提出可能的結果參照物。這些斷言允許開發人員檢測與預期行為的偏差並捕獲當前行為,以防止將來出現破壞該行為的缺陷。

關鍵詞

測試用例生成,斷言生成,基於搜索的軟件測試

1 介紹

當使用諸如 Java 之類的面向對象的語言編寫程序時,通常會執行單元測試以識別軟件中的缺陷並捕獲當前行為,從而檢測到破壞行為的未來缺陷。

測試用例可以自動生成,但是存在一些結果參照物的問題,即如何驗證測試用例的輸出是否為預期的輸出。如果故障導致程序崩潰、死鎖或違反正式規範,則有時可以自動檢測到故障。但是在許多情況下,沒有正式的規範,並且這些錯誤不會導致程序崩潰或死鎖 因此,需要為用戶提供可以手動驗證的小型測試套件(就測試數據和斷言而言),即判讀斷言語句是否與程序的語義一致。

EvoSuite: 面向對象軟件的自動化測試套件生成

圖 1: EvoSuite 可以用作命令行工具或 Eclipse 插件以完全自動化地為 Java 類生成覆蓋測試套件

EvoSuite 可通過系統地生成可儘可能小地實現高代碼覆蓋率並提供斷言的測試套件來自動完成此任務(請參見圖 1)。EvoSuite 使用基於搜索的方法並結合了最新技術,例如混合搜索、動態符號執行和可測試性轉換。此外,EvoSuite 實施了幾種新穎的技術來有效地實現其目標:

整個測試套件的生成:EvoSuite 使用一種演化搜索方法,可以同時針對全部的覆蓋標準對整個測試套件進行演化。關於對覆蓋標準而不是對單個覆蓋目標的優化實現了結果既不會受到順序的不利影響,也不會受到單個覆蓋目標的困難性或不可行性的不利影響。

基於變異的斷言生成:EvoSuite 使用變異測試來生成縮小的斷言集,從而使測試用例揭示的類別中的種子缺陷數量最大化。這些斷言突出顯示了當前行為的相關方面以支持開發人員識別缺陷,並且這些斷言捕獲了當前行為以防止出現迴歸錯誤。

EvoSuite 完全地為單個類或整個項目自動生成這些測試套件,而無需複雜的手動步驟。該工具是免費提供的,可以在命令行上用作 Eclipse 開發平臺的插件,也可以通過 Web 界面使用。

2 整個測試套件的優化

在白盒測試中,當沒有可用的自動智能智慧時,一種通用的系統測試方法是一次為給定的覆蓋率標準選擇一個覆蓋率目標(例如程序分支或控制流路徑),並得出一個行使這個特定目標的測試用例。儘管可行,但該策略存在一個重大缺陷,因為它假設所有覆蓋目標均同等重要,同樣難以實現且彼此獨立。不幸的是,這些假設都不能成立。

許多覆蓋目標根本是不可行的,這意味著沒有測試可以實現它們。這是無法確定的不可行路徑問題的一個實例。即使可行,某些覆蓋目標也比其他目標更難實現。因此,在用於測試的資源有限的情況下,對覆蓋目標順序的幸運選擇可能會導致良好的測試套件,而不幸的選擇可能導致所有資源僅用於少數測試用例。此外,針對特定覆蓋目標的測試用例在大多數情況下也會意外地滿足其他覆蓋目標。同樣,選擇目標的順序也會影響結果–即使考慮了所有覆蓋目標,附加的覆蓋範圍也會影響結果的測試套件。沒有高效的解決方案來預估附加的覆蓋範圍或覆蓋目標的難度。

為了克服這些問題,整個測試套件的生成是一種不針對單個覆蓋目標生成單個測試用例的方法,而是專注於針對整個覆蓋標準的測試套件。對覆蓋標準而不是對單個覆蓋目標的優化實現了結果既不會受到順序的不利影響,也不會受到單個覆蓋目標的困難性或不可行性的不利影響。除此之外,附加覆蓋範圍的概念消失了因為所有的覆蓋範圍都是策劃了的。

EvoSuite: 面向對象軟件的自動化測試套件生成

圖 2:與同時定位一個分支的標準方法相比,使用 EvoSuite 在六個案例研究庫上實現的平均分支覆蓋率:即使進化限制為 1,000,000 條語句,EvoSuite 也可以實現更高的覆蓋率

EvoSuite 將整個測試套件生成作為一種基於搜索的方法來實現,與以前的基於搜索的方法相比有了很大的改進(參見圖 2)。在進化搜索中,使用模擬自然進化的算子(例如交叉和變異)來進化候選解的總體。個體根據他們的適應程度(即估計他們與最優解的接近程度)來被選擇進行繁殖。這種繁殖隨著每一代的適應程度提高而繼續進行,直到解決方案被發現或者分配的資源被用完為止。

EvoSuite 的候選解決方案是一個包括可變數量的個體測試用例的測試套件。其中的每一個測試用例都是一系列可變長度的方法調用,它們依次行使被測單元(UUT)並創建複雜的對象。在兩個測試套件間的交叉是基於隨機選擇的交叉位置交換測試用例。這種類型的交叉消除了由於測試用例的語句之間的依賴性而導致的方法序列交叉的困難。測試套件的變異可能會添加新的測試用例,或者變異單個測試。反過來,通過添加、刪除或更改單個語句和參數也可以變異測試用例。為了幫助生成合適的輸入數據,EvoSuite 還可以在每個預定義的生成數目之後使用集中的局部搜索和動態的符號執行。

個體的適應程度的測量需要考慮到整體的覆蓋標準。例如對於分支覆蓋,適應程度計算 UUT 中所有分支的各個(規範化)分支距離之和。分支距離是一個常用的估計,去估計它距單個執行將分支評估為真或假將有多遠。例如,對於 x 值為 10 的條件 x=42,其分支距離為| 42−10 |= 32 為真。因此,如果一個個體覆蓋了 UUT 的所有分支,則其適應程度為 0。

由於較長的方法序列可以更輕鬆地達到覆蓋範圍的目標,EvoSuite 允許搜索深入到較長的序列中,並且應用了幾種膨脹控制技術以確保個體不會變得太大。 在搜索結束時,測試套件會被最小化,從而僅保留有益於覆蓋範圍的語句。

生成覆蓋測試套件的目的是為開發人員提供一組代表性的測試用例。這使軟件工程師可以檢查並捕獲用自動結果參照物無法捕獲的 UUT 的功能正確性。 為了做到這一點,測試用例需要某種手動的預言,在單元測試的情況下通常是斷言。測試結果參照物問題是傳統白盒測試生成中的主要問題之一。

給定一個自動生成的單元測試,它可以斷言的數量是有限的-斷言的選擇是由它可以對 UUT 及其依賴類的公共 API 進行的可能觀察來定義的。例如,它可以在返回值上編寫斷言、將對象相互間比較或在對象上調用檢查器方法。 因此,綜合成可能的斷言是很容易的。但是,將所有斷言呈現給開發人員是有問題的,因為這些斷言可能太多了而且它們中的許多都是不相關的:僅當開發人員驗證綜合斷言的正確性和確定了問題時,才能檢測到程序當前版本中的錯誤。類似地,一個測試用例可能會在以後的某個節點失效而表現出迴歸失敗,而實際上有太多的斷言可能只是過度具體說明了該測試用例,從而導致錯誤警報。

為了確定重要和有效的斷言,EvoSuite 進行了變異測試。它最初是作為工具 μTest 的一部分開發的,該工具現已成為 EvoSuite 的組件。在變異測試中,將人為的缺陷(變異體)植入程序中,並根據它們可以與原始程序區分開的種子缺陷中的多少來評估測試用例。未檢測到的變異體表明測試套件中存在缺陷,並且在大多數情況下這表明或者應該添加新的測試用例,或者現有的測試用例需要更好的測試結果參照物。如果斷言失敗,變異被檢測到了。相反,如果斷言沒有檢測到程序的任何變體,則它可能與測試用例無關,原則上類似於驗證中虛假滿足的屬性。

在測試用例生成過程之後,EvoSuite 會在未修改的軟件以及該測試用例涵蓋的所有變異體上運行每個測試用例,同時記錄有關通過哪些斷言檢測到哪些變異體的信息。然後,EvoSuite 計算出一組簡化的斷言,這些斷言足以檢測給定測試用例可能揭示的 UUT 的所有變異體。圖 3 顯示了在一組開源庫上評估的這種簡化效果。變異測試的理論表明,僅使用這些斷言就足以檢測 UUT 中的大多數其他可能的故障。

EvoSuite: 面向對象軟件的自動化測試套件生成

圖 3:在 10 個開源項目中進行了評估的基於變異的斷言最小化的有效性。 箱線圖總結了在應用基於變異的最小化的之前(全部)和之後(最小化)的斷言統計。

4 實施

為了生成 Java 類的測試套件,EvoSuite 僅需要被測類及其依賴的 Java 字節碼。對該字節碼進行分析和檢測,並在搜索結束時,EvoSuite 為給定的類生成一個 JUnit 測試套件。EvoSuite 完全自動運行;它可以為整個軟件包生成測試套件而無需用戶干預。

對於給定的包,EvoSuite 一次只考慮一個類,並嘗試生成一個測試套件以最大化該類的分支覆蓋率。EvoSuite 不僅限於原始數據類型,還可以處理任何類的數組和對象。為了產生類的實例,EvoSuite 會考慮產生所需類型的實例的所有可能的方法和構造函數,然後遞歸地嘗試滿足所有依賴關係。EvoSuite 通過將對字符串比較方法的調用替換為對它自己的基於 Levenshtein 距離以計算字符串距離的輔助方法的調用來特別對待 String 類。這允許搜索還可以使字符串向期望值發展以滿足字符串上的條件。作為另外一個改進,EvoSuite 使用常量 primitive 和字符串值池,該池在 Java 字節碼中靜態確定。但是,EvoSuite 當前僅限於單線程應用程序。

由於在測試中反覆執行測試用例以測量適應程度的值,因此被測代碼與環境的交互可能是不被期望的,甚至是危險的:在最佳情況下,隨機序列訪問文件系統上的數據可能會導致混亂,最壞情況下會導致數據丟失;隨機訪問網絡或者數據庫通常也是不被希望的。EvoSuite 提供了自己的安全管理器,可以將其配置為禁止對環境的有害訪問。為了克服隨機值可能導致執行時間過長的問題,EvoSuite 使用超時並在搜索過程中對執行時間長的測試用例進行懲罰。為了克服無法停止的長時間執行的問題,EvoSuite 應用了字節碼檢測、線程處理以及在最壞的情況下重新啟動虛擬機的組合。

EvoSuite 可用作命令行工具,可為單個類或整個包生成測試套件,並生成 HTML 報告以進行進一步分析。為方便起見,EvoSuite 也可以與 Eclipse 開發平臺的實驗性插件一起使用(請參見圖 1),在該插件中只需單擊鼠標即可創建測試用例。最後,EvoSuite 也可以通過 Web 服務使用,無需任何安裝即可進行簡單的實驗。

5 相關的工作

最新的進展使現代測試工具能夠完全自動地為實際規模的程序高效地導出測試用例。通常這些測試用例是隨機生成的,目的是檢測程序崩潰或查找違反合約的情況。為了改進通常實現低代碼覆蓋率的隨機測試及其變體,已經提出了基於動態符號執行的技術。與此類工具相比,EvoSuite 不僅嘗試查找違反自動的結果參照物的測試用例,而且還旨在生成緊湊的測試套件來實現較高的代碼覆蓋率,以便開發人員可以使用它們來評估功能的正確性。

使用代碼覆蓋率時,一種常見的系統方法是一次選擇一個覆蓋率目標(例如程序分支或控制流路徑),並得出一個可以實現該特定目標的測試用例。 相比之下,EvoSuite 同時開發針對所有覆蓋率目標的整個測試套件,從而帶來了第 2 節中討論的多項優勢。

一種流行的單元測試 C#代碼工具是 Pex,它使用動態符號執行為參數化測試用例生成輸入值。Pex 還可以基於方法的返回值來生成斷言,但是在需要複雜方法序列的類方面受到限制。有幾種 Java 生成 JUnit 測試用例的工具:Randoop 以其易用性而聞名,但與 EvoSuite 相比,它隨機地測試軟件而沒有涵蓋複雜代碼結構的指導,並報告了違反預定義合約的情況。

TestFul 和 eToc 可能是最相關的工具。兩者都使用基於搜索的方法來生成 JUnit 測試套件以最大化結構覆蓋範圍。但是 eToc 是一種舊工具,幾年來一直沒有更新,因此沒有測試數據生成方面的最新進展。另一方面,TestFul 在許多關鍵細節上與 EvoSuite 不同,並且不是完全自動化的。例如,TestFul 要求為每個被測類手動編輯 XML 文件,這導致其經驗評估僅針對 15 個類。另一方面,由於 EvoSuite 像 Randoop 一樣是全自動的,因此有可能在數千個類中對其進行評估。

在斷言生成方面,Randoop 允許對源代碼進行註釋以標識要用於斷言生成的觀察者方法。Orstra 根據觀察到的返回值和對象狀態生成斷言,並添加斷言以根據這些觀察結果檢查將來的運行。儘管可以使用這種方法來獲得有效的結果參照物,但它們不能用來標識這些斷言中的哪些實際上有用,因此只能在迴歸測試中找到這種技術。相反,通過 μTest 工具,EvoSuite 使用變異測試來選擇有效的斷言子集。

6 結論

EvoSuite 是一種可自動為 Java 程序生成測試套件以實現高代碼覆蓋率並提供斷言的工具。EvoSuite 實現了幾種新穎的技術,從而導致更高的結構覆蓋率以及基於 seeded 缺陷的高效斷言選擇,這是其他 Java 工具缺少的一項關鍵功能。

EvoSuite 當前支持將分支覆蓋和變異測試作為測試目標,但我們也在致力於添加更多例如基於數據流的標準的標準,並且我們研究的另一個重點是生成更具可讀性的測試用例。

致謝

本文由南京大學軟件學院 2020 級碩士生唐昊傑翻譯轉述。 感謝國家重點研發計劃(2018YFB1003900)和國家自然科學基金(61832009,61932012)支持!


分享到:


相關文章: