小心!做UI自動化一定要跨過這些坑
二、問題分類及目標明確
筆者將以上所有的問題簡單分成三類:設計類、環境類、細節類。一個好的設計模式,能夠避免一部分問題;一套好的環境,可以讓我們從乏味的維護工作中解脫;精益求精的細節,讓測試用例更加可靠穩定。
圖一UI自動化常見問題
填掉這三類坑,基本上就獲得了一套低成本高產出、少量維護、穩定可靠的UI自動化用例集。
三、設計類問題分析與解決
“才剛寫完用例,怎麼開發大哥又改了UI了?”
“測試框架自己有bug,我改用例也沒用啊……”
這類問題,我們需要從根上治。UI自動化開發,也應該是嚴謹的開發工作,它也需要設計模式,也是磨刀不誤砍柴工。這裡的設計,主要包括選工具、框架分層等。很多前輩都分析過UI自動化各類工具的優缺點,對工具選用筆者不再贅述。主要依託uiautomator來介紹下筆者認為比較巧妙的用例框架設計。
1、優化測試程式碼框架
無論你選擇appium、uiautomator、robotium還是espresso,剛入門時,看到的sample應該大致都是這樣的。
圖二 uiautomator和espresso邏輯樣例
問題在哪裡?這些sample過於簡單,都只教了我們UI自動化三元素:怎麼查詢元素、怎麼操作元素、怎麼校驗結果。如果我們按照大多數分享帖或GitHub sample來寫作自己的case。最後這種沒有任何設計模式的框架,肯定會面臨重構。拿上面的espresso來說:
1.假如action_save這個id開發改了,而你的用例集中,有30個步驟用例到了這個id,一個個去改,是不是要瘋?
2.不厭其煩的重複寫onView(withXX(xxx)).perform(click())這一長串,你不煩?
筆者是如何做的呢?
分層設計和PageObjects模式。這兩個方法,基本解決了筆者遇到的圖一中所有的設計類問題。
圖三 框架設計建議
按照圖三進行分層設計後,得到如圖四的測試程式碼包。
圖四 分層後的用例框架
PageObjects模式發源於selenium社群,它的目的是減少重複程式碼,當開發修改UI時,測試只需在有限的位置修改程式碼。如果大家想深入瞭解PageObjects,請參照如下wiki:
我們來看一下,現在手管首頁Page包中的程式碼和頁面。
圖五 手管首頁Page層部分程式碼
回憶一下上面的google提供的sample,再對比引入分層設計和PO模式前後的程式碼,點選圖五中的一鍵加速:
圖六 引入PO前後程式碼對比
帶來的好處,當然不僅僅是業務用例程式碼更清爽。
1、通過將查詢和操作封裝到基礎層中,這部分程式碼就具體業務無關了,即使拿到其他產品中也可以複用;
2、通過page層的分離,所有的與業務相關的id,text等都被限定在了page包中,哪怕開發改了UI,修改page包特定的頁面中對應的元素就好了。
3、對page包進行合理的業務拆分,比如將手管分成 MainPage(主頁),SoftwareManagerPage(軟體管理頁),WiFiManagerPage(WiFi管理頁)等,在開發改了某個具體業務的介面後,測試能夠迅速知道測試程式碼需要改哪裡。
2、相容資源混淆的測試程式碼
除了整個框架的設計,有時候一些小問題也可以經過巧妙設計。比如資源混淆的問題。
圖七 資源混淆
如圖七,在手機管家的釋出包中,用uiautomatorviewer dump下來發現,一鍵優化的button,其resource-id是o3,但其實開發coding時,定義的id顯然不會用這種沒有任何字面意義的代號,它在混淆之前叫optimize_button。
純黑盒的UI自動化,也許你會摒棄optimize_button,直接寫o3,但這樣顯然不夠科學,既帶來了嚴重的程式碼可讀性問題,同時一旦版本迭代,混淆變了,o3也許就變成了o4。或者你會讓開發給你測試的包,不要混淆,但如果想用UI自動化測試已釋出的apk呢?
解決該問題,也得從PageObjects說起。回到圖五中OPTIMIZE_BTN的定義,這個靜態變數並未在page中初始化,只有一個@FindBy的註解。其實,在框架層驅動測試開始前,框架會先呼叫如下圖八所示的setAllField來初始化所有的page頁面。
1、如果被測應用未混淆資源,該方法只是將@FindBy中的值賦值給Field。
2、如果被測應用已混淆資源,該方法則會從mObfuscationMap(未貼出全部程式碼,實際是解析一個開發提供的混淆表,以原始id為key,混淆id為value的HashMap)中讀出對應的id對應關係,將混淆後的id賦值給Field。
圖八 Page層動態初始化
四、環境類問題分析與解決
“adb怎麼這麼不穩定啊,老是斷!!!”
“明明介面上有這個元素,怎麼就是查不到呢?”
“這破手機,能不能別老是系統彈框……”
“這手機真是渣,adb screencap截個圖,居然要三分鐘才返回!”……
引子中提到的這些問題,根據經驗,多半你的環境執行環境還不夠穩定。
1、ADB相關問題已知的ADB不穩定原因如:電壓不穩,各類手機助手的干擾,系統版本與ADB版本不匹配、ADB crash等等。如果我們迎難而上,去重寫ADB,投入將無限擴大。所以建議主要的解決方案,還是儘量規避。
1)、選用可靠硬體規避電壓不穩定。github上的STF專案組有過成熟的經驗,選用效能更優的USB分接器,電壓和可靠性會有更穩定的表現。(附上鍊接,wiki Recommended hardware一節中有不同硬體詳細的效能對比:https://github.com/openstf/stf)。
2)、遮蔽各類手機助手的干擾。91助手、豌豆莢等,基本都在adb上做了二次開發,它們會與原生adb間有相容性問題。建議直接使用Linux/MAC系統作為執行環境以遮蔽這類干擾。
3)、降低用例在執行過程中對環境的依賴。Appium這類自動化工具,每一個測試步驟都需要PC端的appium server和測試手機端的bootstrap互動訊息。測試過程中只要USB連線不穩定,都會導致整個測試套的失敗。所以筆者認為,使用更原生的uiautomator會是更好的選擇;同時,測試過程中的日誌、截圖等,也儘量在測試手機上做持久化。
2、彈框問題的解決
許可權彈框,是手管UI自動化中的一個大坑。如下圖,是測試手管過程中,在華為手機上遇到的部分許可權彈框。這些彈框,並不會用例每次執行都彈出,不同廠商的彈出框也不一致。顯然點選彈框的邏輯,寫在case邏輯中,只會導致自動化變得更復雜更不穩定。
圖九 各類許可權彈框
uiautomator的watcher,能夠完全實現點選彈框和用例邏輯的解耦。當前筆者的實現邏輯是,監聽彈框上的某個控制元件,當該控制元件出現時,執行action來點選掉其中的取消或確定按鈕。這樣,用例就只需關注業務邏輯,而任何時候的彈框,都由watcher來自動點選。如下圖中,checkForCondition關注條件,action是操作。
圖十 查詢型Watcher
將所有的watcher分不同的手機廠商進行註冊後,再呼叫runWatchers(),然後再執行用例。該方法可以在@BeforeClass中或者RunListener的testRunStarted中呼叫。當然,如果某個用例不想某個具體的彈框被watcher點選掉,也可以呼叫removeWatcher()反註冊。
圖十一 註冊監聽器
Watcher並不能解決所有的彈框問題。例如,在開啟WiFi的場景中,由於WifiManager的setWifiEnabled和UI上的彈框點選是同步的(意思是呼叫了setWifiEnabled之後,如果介面上不點允許,該方法是不會返回的),使用上面的watcher方式並不會點選WiFi許可權申請的允許。這時,就需要用到執行緒方式來解決(如下圖十二),呼叫setWifiEnabled前,先啟動一個執行緒等待彈框彈出。
圖十二 多執行緒方式點掉彈框
五、細節類問題分析與解決
“我除錯的時候這個用例還是通的,放到daily裡面跑就不通,到底怎麼回事嘛!”
出現上述問題,多半是因為我們的用例細節不夠嚴謹。這類問題,往往決定著我們自動化用例集,是不是能從90%的case通過率,提升到100%。
1、順序邏輯的用例
自動化相比手工,它只會關注code告訴它的驗證點,所以選擇邏輯在用例中應該是禁用的。如下圖十三中右側的case,如果用例執行到if中,也許else流程中存在BUG,反之亦然。此時考慮拆分用例,左側才是理想的用例邏輯。
圖十三 用例邏輯
另外,寫作case時,一定要牢記,只有我們告知程式要assert,它才會去assert。查詢,操作,斷言,UI自動化三要素缺一不可。
2、解耦的用例
在testng中,會提供dependsOnMethods註解,似乎在鼓勵寫作用例時,使用用例間依賴。但筆者認為,用例間的依賴,會帶來不必要的維護成本。只有高度解耦的用例邏輯,才能夠更加健壯的支撐用例執行順序調整、用例增刪、出現異常場景後,A用例失敗不會導致B用例也失敗。
3、優化等待
有時候會遇到以下場景,雖然原生的自動化工具提供了等待元素可見的方法,但使用起來,還是無法真正等到元素可見。針對這個問題,如下圖的waitCondition方法是一個不錯的方案,它相對於thread.sleep來說,更節省時間。
圖十四 反覆等待方法
4、不用絕對座標點選絕對座標點選,在不同尺寸螢幕上無法相容。
第一方案應該是,推動開發對需要用到的控制元件新增ID或Accessibility。但根據經驗還是會有一些場景需要用到座標點選:
1、考慮投入產出比,為所有控制元件新增id的成本過高;
2、動態佈局新增的ID都一樣;
3、存在非xml佈局的介面(程式碼中直接佈局)。
這時,筆者依然不建議mDevice.click(100,200)這樣的座標點選。有以下兩種值得一試的方案。
1、找到相鄰控制元件座標,計算當前控制元件的絕對座標。如下圖十四,uiautomatorviewer中點選右上角警告小三角,會得到有一些元素(黃色控制元件),是可能無法找到的。而使用相對座標就是說,我們可以獲取它相鄰控制元件的座標,然後減去或加上一個比較小的px值,再點選計算後的座標即可。
圖十五相對座標
2、使用螢幕尺寸計算相對位置。在測試開始,將螢幕尺寸存下來,使用百分比的方式計算得到需要點選的位置。如下程式碼,點選【50%螢幕寬度,80%螢幕高度】的位置。
CODE: mDevice.click( screenWidth/2 , screenHeight*80/100 )。
六、總結
UI自動化測試是一門學起來很簡單,用起來很麻煩的測試技術。
想要入門,兩週就可以瞭解清楚uiautomator或espresso這類工具。UI自動化,無非就是查詢元素、操作元素或裝置、驗證結果。這三個步驟迴圈多次,就是一個用例。
但要用好,併產出能效,需要走的路其實很長。由於篇幅限制和知識有限,這裡不可能把所有的問題一一列出。對於所有這些問題,無非兩個思路:一是繞過,二是解決。
1、選一個儘量簡化,儘量底層的工具(uiautomator或espresso),從根上繞過一些工具會存在的問題;
2、採用良好的設計模式,讓自己的框架更穩定,生命週期更長,維護成本更低;
3、明知道會耗費很多時間精力,收效卻很小的環境問題,儘量繞過;
4、優化用例邏輯和細節,使之穩定可靠,更能說服別人相信自動化的測試結論。
七、最後
不知您是否也曾在UI自動化過程中遇到過難以解決的問題呢?
歡迎大家留言討論。
也祝願大家在UI自動化的道路上越走越順!
長按指紋識別圖中的二維碼,獲取更多測試乾貨分享!
將我們公眾號置頂 不會漏掉我們的原創乾貨哦!