摘要
眾所周知,UI 測試是非常困難的,尤其是在現在這種開發週期越來越快的情況下。手動 UI 測試非常繁瑣、昂貴且容易出錯。而自動化的 UI 測試在編寫和維護上成本又很高。
本文介紹了 AppFlow,一個用於合成高度健壯、高度可重用的 UI 測試的系統。它利用機器學習來自動識別常見的螢幕和控制元件,從而使開發人員不必編寫臨時的程式碼邏輯即可在測試中使用它們。它使開發人員能夠為某類應用的主要功能編寫模組化的測試庫(例如,針對購物應用的“新增到購物車”功能進行測試)。然後,它可以通過合成庫中的測試指令碼,從而快速測試同一類別中的新應用。通過專注於特定型別應用的主要功能模組,AppFlow 幾乎不需要人工干預。
我們在購物和新聞類別中的 60 個主流 app 上進行了評估,其中,我們對 BBC 和 JackThreads 應用分別進行了案例研究,對 Wish 應用中的 15 個主題進行了使用者調研。結果表明,AppFlow 可以準確識別螢幕和控制元件,合成高度健壯且可重複使用的測試。AppFlow 合成的測試覆蓋了 Jackthreads 中 46.6%的自動化測試,並將測試新應用的工作減少了 90%。有趣的是,儘管所有被測 app 都已公開發布並稱通過了全面測試但我們依舊發現了八個錯誤,包括七個功能錯誤。
1 介紹大多數應用程式旨在與人類進行互動,因此測試 app 的功能、效能和使用者介面(UI)相關的其他關鍵方面至關重要。但是,眾所周知,UI 測試極具挑戰性。手動測試的優點是可以測試真實的使用者體驗,但是缺點是操作繁瑣,成本高且容易出錯。自動化測試原本應該是一種更好的替代方法,但是當今行業中的自動化測試需要大量的開發人員輔助,而且很少有公司具備進行自動化測試的技能或資源。
UI 自動化測試通常是基於指令碼的測試。要進行自動化測試,開發人員必須投入高昂的初始成本來編寫測試指令碼以及在應用程式 UI 更新時維護測試指令碼。雖然這些任務從表面上看似乎很容易,但是由於應用程式 UI 是為人類智慧而設計的,而測試指令碼卻是低階的單擊單擊指令碼,因此存在很多難以實現的功能。例如,向購物車新增商品的按鈕無論是“新增”字樣、加號或是圖示,我們人類都可以很容易地識別出,但測試指令碼通常是通過開發人員提供的座標來定位按鈕。例如,當按鈕從“新增”字樣演變為圖示時,或者座標因螢幕尺寸等裝置因素而不同時,這種識別方式很容易變得不正確。測試的錄製和重播減少了編寫測試的成本,但是錄製的測試很少能立即可用,UI 的更新將導致測試的重新錄製。
當今越來越快的開發週期加劇了自動化測試的挑戰。然而不幸的是,現有的自動化測試框架中很少有旨在跨 app 重用測試指令碼的。首先,儘管相同類別的應用程式在流程上有很多相似之處,但它們的螢幕和控制元件可能具有截然不同的設計。因此,一個應用程式的測試指令碼通常無法找到另一個應用程式的控制元件。其次,相同類別的應用程式可能仍然具有不同的流。例如,一個應用程式的登入流程可能只包含登入螢幕,而另一個應用程式可能首先顯示歡迎螢幕。一個應用程式的“新增到購物車”流程可能要求使用者首先訪問商品詳細資訊螢幕,但另一個應用程式可能允許使用者將搜尋結果中的商品直接新增到購物車。這些細微的差異會阻止在不同應用程式上直接重用測試指令碼。
本文介紹了 AppFlow,這是一個用於合成高度健壯、高度可重用的 UI 測試的系統。它使開發人員能夠為特定類別的應用編寫模組化的 UI 測試庫。該庫可以是開源共享的,也可以是儲存在測試雲服務中的。然後,當開發人員想要測試同一類別中的新應用時,他可以快速從庫中的模組化測試中合成一個完整的測試,從而極大地提高了生產率。
通過專注於應用的主要功能,AppFlow 可以提供冒煙測試或是為原始碼的更改構建驗證測試,則幾乎不需要任何手工工作。先前的工作表明,這種測試,即使是不完整的測試,也可以為開發人員提供快速反饋,並幫助他們在 bug 產生更大影響之前及早修復 bug。開發人員可以選擇自定義 AppFlow 以新增針對特定應用的測試或覆蓋預設值以執行完整的迴歸測試。
AppFlow 中的一個關鍵思想是一種用於識別螢幕和控制元件的機器學習方法。AppFlow 不依賴開發人員的程式碼邏輯,而是通過訓練集訓練分類器,訓練集包括帶標註的螢幕及控制元件。訓練集可以來自開發人員社群,並且 AppFlow 提供了多個實際程式來簡化這種一次性的資料收集。訓練分類器後,AppFlow 使用它來將變化的螢幕和控制元件對映到規範的螢幕和控制元件。例如,它將登入螢幕上帶有“使用者名稱”、“您的電子郵件”或“[email protected]”的文字編輯框都對映到代表使用者名稱控制元件的 signin.username。這種機器學習方法使 AppFlow 測試可以引用規範的螢幕和控制元件,而不是針對特定應用程式的螢幕和控制元件,因此具有很多優點。首先,只要新的 UI 介面可以被 AppFlow 識別,app 的 UI 就可以不斷更新而不會破壞測試指令碼。其次,應用程式使用者介面可以適應各種裝置因素(例如螢幕尺寸)而不會破壞測試。第三,規範的螢幕和控制元件實現了對應用程式的抽象,從而使跨應用程式共享測試變得容易。第四,AppFlow 能夠識別螢幕,從而使開發人員能夠專注於測試螢幕的特定流程,而無需編寫太多程式碼來啟動 app,或在測試結束後將應用程式恢復到以前的狀態。這點對於可重用性起著至關重要的作用。
AppFlow 的第二個主要思想是通過可重複使用的獨立測試來發現應用的行為,並從中合成完整的測試。我們將可重複使用的獨立測試稱之為“流”。為了測試諸如“在商品詳細資訊頁面上,使用者可以將商品新增到購物車”之類的功能,開發人員編寫了包含以下三個組成部分的流程:(1)測試的前提條件,例如“應用程式必須在商品上詳細資訊螢幕;”(2)測試的後置條件,例如“應用程式必須在購物車螢幕上”;(3)進行測試的實際步驟,例如單擊“新增”按鈕。前提條件和後置條件在本質上類似於霍爾邏輯,並且可以在應用程式狀態上包含自定義條件,例如 loginin = true(即使用者必須已登入)。“流”具有雙重用途:可用於測試應用程式是否正確實現了此功能,並且可用於將應用程式導航到測試其他功能所需的狀態。具體來說,在給定一個流庫的情況下,AppFlow 會動態合成以下完整的測試:啟動應用程式,識別其狀態,找到滿足先決條件的啟用流,執行每個流,併為到達的每個新狀態重複以上步驟。
AppFlow 的合成方式有兩個主要好處。首先,它極大地簡化了測試的建立,因為開發人員不再需要編寫特定程式碼來將應用程式置於特定狀態或隨後清除該狀態。其次,模組化可實現測試重用。如果將測試指定為一個整體,則由於測試物件場景或是實現該場景所需步驟的變化,測試將幾乎無法重用。相比之下,模組化測試可以通過適當地合併以適應特定應用的行為。例如,我們可以建立一個測試庫,其中包含兩個帶有或不帶有歡迎螢幕的登入流程以及兩個通過或不通過商品詳細資訊螢幕的購物車流程。然後,AppFlow 可以為我們要測試的新購物應用程式合成出正確的測試。此外,它還允許 AppFlow 適應應用程式行為的更改。AppFlow 可以發現應用程式的新行為,並自動為它們和完成相應的測試。
由於 Android 平臺的廣泛採用和開發人員面臨的嚴峻市場競爭,我們為 Android 平臺實施了 AppFlow,但是這些想法和技術很容易應用於一般的 UI 測試。AppFlow 編寫流程的語言是 Gherkin 的擴充套件,Gherkin 是一種人類可讀的領域特定語言,用於描述應用程式行為。
我們對 AppFlow 進行了四組實驗。首先,我們為兩個應用類別建立了測試庫,並對 40 個購物應用和 20 個新聞應用進行了評估。其次,我們對 BBC 新聞應用程式進行了案例研究,該應用程式具有兩個截然不同的版本,通過實驗驗證 AppFlow 合成的測試是否對更改具有魯棒性。第三,我們對 Wish 的 15 個主題進行了一項使用者研究,以比較 AppFlow 和現有測試框架。第四,我們分析了 JackThreads 應用程式開發人員的手動測試計劃,並量化了 AppFlow 可以自動合成的測試數量。結果表明,AppFlow 可以準確識別螢幕和控制元件,合成高度健壯和可重複使用的測試,涵蓋所有針對 Jackthreads 的自動測試的 46.6%,並將測試新應用的工作減少了 90%。有趣的是,儘管所有被測 app 都已公開發布並稱通過了全面測試但我們依舊發現了八個錯誤,包括七個功能錯誤。
本文做出了三個主要貢獻:(1)用於合成高度健壯、高度可重用的測試的 AppFlow 系統;(2)我們的技術利用機器學習來識別螢幕和控制元件的魯棒性和可重用性;(3)我們對 60 個購物和新聞應用程式進行了評估,這些應用程式產生了 2944 個測試並發現了 8 個錯誤。AppFlow 的原始碼和測試庫可在 github.com/columbia/appflow 中找到;AppFlow 的資料集可在 github.com/columbia/appflow-dataset 上獲得。
Figure 1: 流程:“新增到購物車”。
2 綜述本節首先提供一個簡潔的示例,以演示如何編寫 AppFlow 測試,然後描述其工作流程。
2.1 例子假設開發人員想測試購物應用程式的流程“向空購物車中新增商品會清除“購物車為空”訊息”。圖 1 顯示了 AppFlow 中此測試的示例。流程的前提條件已給出。啟用此流程的螢幕應該是“詳細資訊”螢幕,這是顯示專案詳細資訊的規範螢幕。此螢幕幾乎存在於所有購物應用程式中,因此使用它指定條件不僅可以簡化對該流程的理解,還可以在其他購物應用程式上重用此流程。這裡的“螢幕”是 AppFlow 內建的可見屬性。為了執行此流程,AppFlow 確保必須滿足流程的前提條件,即前提條件中指定的所有屬性都必須具有相應的值。
接下來,該流程相應按鈕進行了兩次單擊。與使用手寫的的傳統測試指令碼不同,AppFlow 測試使用由測試庫匯出的規範元件,且 AppFlow 利用機器學習將實際元件與規範元件進行匹配。
這個簡單的示例顯示了 AppFlow 的一些關鍵優勢。即使對於非開發人員(例如產品經理),此流程也易於理解。AppFlow 會使用機器學習方法自動識別所使用的規範螢幕和控制元件,從而使測試對 UI 設計的更改具有魯棒性,並且可在不同應用程式之間重複使用。該系統允許開發人員僅描述要測試的流程,而無需編寫樣板程式碼將應用程式帶到專案詳細資訊螢幕。
2.2 工作流程圖 2 顯示了 AppFlow 的工作流程。它分為兩個階段:第一階段(通常是一次),為新類別的應用程式做準備,第二階段,應用 AppFlow 來測試該類別中的每個新應用程式。
Figure 2: AppFlow 的工作流程。此處的簡筆圖代表開發人員的干預。
2.2.1 為新的應用類別做準備
首先,他們使用 AppFlow 建立一個測試庫,其中包含該類別的通用流程,並在此過程中定義規範的螢幕和控制元件。其次,他們使用簡單的實用程式來捕獲規範螢幕和控制元件的資料集並對其進行標記。有時,不同類別的應用程式共享相似的螢幕(例如,登入螢幕),也可以新增其他應用程式類別的這些示例。有了這個資料集,
AppFlow 將從每個樣本中提取關鍵特徵,並訓練分類器。
2.2.2 測試一個新的應用程式
為了測試新應用,開發人員要做兩件事。首先,他們為應用程式定製測試庫。機器學習無法始終識別每個規範的螢幕和控制元件, 為了糾正偶然的錯誤,開發人員執行 AppFlow 來發現機器學習中存在的錯誤並覆蓋它們。此外,開發人員還為庫中使用的變數提供值,例如測試使用者名稱和密碼。開發人員還可以新增自定義流程來測試特定於應用程式的行為。
其次,開發人員在應用程式上執行 AppFlow 來記錄初始測試結果。測試庫通常包含多個變體流程,例如從歡迎螢幕或選單螢幕登入。AppFlow 執行所有流並報告每個流的結果,讓開發人員確認哪些流應該成功,哪些流應該失敗。
完成這兩個設定步驟後,開發人員可以定期測試新版本的應用程式是否存在迴歸。AppFlow 執行類似的過程來為每個新的應用程式版本合成完整測試,並將結果與上次執行的結果進行比較。
3 識別規範的螢幕和控制元件直觀地講,用於類似目的的螢幕和控制元件應具有相似的外觀,並具有相似的名稱。但是,由於跨應用程式的差異以及同一應用程式隨時間的演變,簡單的規則無法正確識別它們。例如,“登入”螢幕上的“登入”按鈕可能包含“登入”、“讓我登入”,甚至是顯示箭頭的圖示。基礎 UI 物件通常具有“Button”的類名,但是有時可以將其更改為“TextView”甚至“RelativeLayout”。AppFlow 無需使用臨時的手動編寫規則來識別控制元件,而是利用機器學習組合各個來源的資訊,因此功能更加強大。
特徵選擇是準確識別的關鍵,它佔用了我們很多精力。我們嘗試了多種功能組合,並通過以下方法進行了解決。對於每個 UI 物件,識別的特性包括其關鍵屬性,例如描述文字,大小,是否可單擊。在最終特徵向量中,所有特徵都將轉換為 0 到 1 之間的值。使用最大值對大小等數字特徵進行歸一化。布林功能(例如,控制元件是否可單擊)將直接轉換為 0 或 1。UI 佈局通過預定樹遍歷轉換為文字。圖形特徵以兩種方式處理。按鈕圖示具有特定含義,因此可以通過計算其定向梯度的直方圖將它們轉換為特徵向量。其他圖形功能可通過 OCR 轉換為文字。所有文字功能(包括從 UI 佈局和圖形轉換而來的文字功能)均使用術語頻率-反向文件頻率進行轉換。直觀地講,如果術語出現在更少的文件中,而在文件中出現的次數更多,那麼其權重則更高。
4 編寫測試流程本節首先描述我們對 Gherkin 語言所進行的擴充套件,然後說明有關建立測試庫的一些細節。
4.1 編寫流程的語言AppFlow 所使用的流程語言遵循 Gherkin 的語法。Gherkin 是由行為驅動的開發工具 cucumber 使用的需求描述語言。而 cucumber 被廣泛用於移動應用程式的自動化測試框架 Calabash 中,因此,我們選擇擴充套件 Gherkin 而不是擴充套件另一種語言,因為移動開發人員應該已經對此有所了解。
在 AppFlow 中,每個流都被編寫為 Gherkin 中的場景,其中先決條件以 Given 為字首,測試步驟以 When 為字首,後置條件和效果以 Then 為字首。與 Gherkin 中使用自然語言作為條件和步驟的應用程式不同,AppFlow 使用可見的和抽象的屬性。
下面我們顯示了四個流程示例。第一個流程測試使用者可以使用正確的憑據登入:
第二個流程測試登入使用者是否可以從主螢幕進入購物車:
當購物車為空時,第三個流程測試“購物車為空”訊息是否顯示在“購物車”螢幕上:
最後一個流程要求當購物車為非空時,將購物車清空,並期望看到“購物車為空”訊息:
4.2 建立測試庫現在,開發人員為同一類別中的不同應用程式編寫了類似的測試用例,匯出產生了大量的多餘工作。通過測試庫以及 AppFlow 能夠識別規範螢幕和控制元件的功能,開發人員可以共享他們的工作,從而大大提高了生產率。
為測試庫編寫流程有兩個注意之處。首先,開發人員需要確定要在測試庫中包含多少個流。在建立定製流的成本和建立定製的成本之間需要權衡。包含更多的流,測試庫就更有可能包含罕見的測試行為,因此需要的自定義流也更少。另一方面,包含更多的流通常意味著更罕見的控制元件,這些控制元件的應用樣本更少。因此,這些部件可能具有較低的分類精度,這使得它們需要更多時間來進行自定義。其次,相同的功能在各個應用程式之間的實現可能略有不同。如前所述,一個應用程式的新增到購物車流程可能要求使用者首先訪問商品詳細資訊螢幕,但是另一個應用程式可能允許使用者將搜尋結果中的商品直接新增到購物車。儘管從概念上講這些流程是對購物車新增功能的相同測試,但是它們需要以不同的方式實現。因此,AppFlow 支援可以具有多個變體流程的測試。
5 將測試庫應用到新應用開發人員分兩個階段將測試庫應用於其應用程式。首先,在設定階段,當第一次將測試庫應用到應用程式時,需要配置並自定義測試庫,特別是為測試變數分配必要的值。開發人員也可以在此階段新增自定義流程,以測試特定的應用行為。之後,執行 AppFlow 來合成測試並記錄通過和失敗的結果。請注意,失敗的流並不一定表示錯誤。相同的功能可能以不同的方式實現,因此失敗的流程可能只是意味著它不適用於需要被測試的應用。
其次,在增量階段,應用該測試庫來測試應用程式的新版本。具體來說,AppFlow 在新版本上運行鍼對先前版本合成的所有測試,重試先前失敗的所有流,並將結果與先前結果進行比較。差異可能表現在一些先前通過的流現在失敗,而其他先前失敗的流現在通過。然後,開發人員可以修復錯誤或確認要進行某些更改。如果需要,可以進一步自定義庫。每次增量執行所需的時間都比設定階段要少得多,因為 AppFlow 會記住為先前版本合成的測試。
AppFlow 從應用程式的初始狀態開始,重複執行測試流,並使用這些流達到的新狀態擴充套件狀態轉換圖。當不再有活動流時,該過程結束。通過組合從初始狀態開始到結束的流鏈,可以對流進行全面測試。
6 實現AppFlow 使用 15979 行 Python 程式碼為 Android 平臺實現。它使用 scikit-learn 進行機器學習,並使用 Tesseract 從影象中提取文字。
6.1 捕獲屏幕布局AppFlow 使用 UIAutomator 捕獲當前的屏幕布局。AppFlow 還通過 WebView 遠端除錯協議與應用程式的 WebView 進行通訊來捕獲應用程式的嵌入式網頁。與 UIAutomator 相比,嵌入式網頁內的控制元件提供了更多詳細資訊。
6.2 捕獲佈局後的處理UIAutomator 返回的佈局包含多餘或不可見的檢視,這會降低 AppFlow 的螢幕和控制元件識別的準確性。因此, AppFlow 使用多種轉換對佈局進行處理。例如,將一個僅帶有單個孩子節點的容器刪除,只保留其孩子節點。另一種轉換是使用光學文字識別來查詢和刪除隱藏的檢視。它從每個控制元件的快照中提取文字,並將該文字與控制元件的 text 屬性進行比較。如果差異太大,則將該部件標記為不可見。如果控制元件的所有子級都不可見,則 AppFlow 還將控制元件也標記為不可見。我們的結果表明,這種處理可以安全地刪除多達 11.5%的控制元件。
7 侷限性和未來工作7.1 AppFlow 本身的限制AppFlow 旨在大大減少進行自動化 UI 測試的人工工作,但 AppFlow 尚未能完全替代手動 UI 測試:眾所周知,到目前為止,由於使用者體驗非常主觀,因此自動化 UI 測試無法完全替代手動 UI 測試。但是,早期發現應用中存在的錯誤可以提高開發人員的生產力和軟體品質,從而間接減少手動測試的工作量。
因此,AppFlow 旨在生成常見的自動測試方案。因此,測試庫應僅包括通用流,而不是每個可能的流。可以編寫自定義流程來測試特定應用程式的功能。另一方面,必須有足夠的自定義流或通用流,才能使 AppFlow 合成可執行測試。例如,如果沒有適用的登入流程,則 AppFlow 無法到達要求使用者登入的流程。我們的評估顯示,通常僅需要少量的自定義流程。
AppFlow 的測試庫中的流程應僅引用規範的控制元件,這可能會限制其執行的檢查並降低其有效性。AppFlow 專注於測試核心功能,正如我們已經顯示的那樣,這些功能在應用之間廣泛共享,並且只能使用規範的控制元件進行測試。隨著測試庫的發展,可以新增更多的規範螢幕,並且可以定義更多的規範控制元件,因此測試可以更加有效。
7.2 機器學習中的分類錯誤AppFlow 利用機器學習來識別螢幕和控制元件。由於本質上是統計的,因此機器學習有時會產生錯誤,從而需要開發人員提供匹配器。當應用程式更新時,這些匹配器也可能需要更新。即使測試的功能未正確實現,流也可能通過。例如,假設一個流程檢查了某個規範的控制元件,並且軟體更新刪除了該控制元件,則如果機器學習錯誤地將另一個控制元件識別為規範控制元件,則該流程仍然可以通過。機器學習分類錯誤只會導致最簡單的流程出現問題,因為依賴於與該控制元件互動的任何流程都可能會中斷,從而向開發人員指出了問題。但是,此問題不僅限於 AppFlow,因為傳統的測試指令碼通常使用脆弱的規則來匹配控制元件,因此它們具有相同的問題。相反,由於 AppFlow 使用機器學習來識別規範的 UI 元素,因此隨著機器學習的準確性提高,此問題也將得到緩解。
8 總結在本文中,我們介紹了 AppFlow,這是一個用於合成高度健壯、高度可重用的 UI 測試的系統。AppFlow 通過識別同一類別中的應用程式具有的共同點來實現這一目標。它利用機器學習來識別規範的螢幕和控制元件,並提供了一種用於從模組化測試庫中合成完整測試的系統。
我們在購物和新聞類別中的 60 個流行應用程式上評估了 AppFlow,其中,在 BBC 新聞應用程式和
JackThreads 購物應用程式中進行了兩個案例研究,在 Wish 購物應用程式中對 15 個主題進行了使用者研究。
結果表明,AppFlow 可以準確識別螢幕和控制元件,合成高度健壯且可重複使用的測試。其合成的測試覆蓋了 Jackthreads 中所有自動化測試的 46.6%,並將測試新應用的工作減少了 90%。我們還在評估中發現了 8 個錯誤,其中有 7 個是功能錯誤,包含這些錯誤的應用程式已公開發布並且應該進行了全面的測試。
感謝國家重點研發計劃(2018YFB1003900)和國家自然科學基金(61832009,61932012)支援!