導語 :“才剛寫完用例,怎么開發大哥又改了UI了?” “維護這些破用例的時間,都夠我手工測三遍了,真的有意義么?” “這破手機,能不能別老是系統彈框……”
UI自動化,在移動互聯網時代的今天,一直都是在各大測試社區最為火爆的一個TOPIC。甚至在測試同行面前一提起自動化,大家就會自然而然的問:“恩,你們是用的什么框架?appium?還是robotium?”
其實在筆者看來,UI自動化是一個ROI較低的測試項(ROI即return on investment,中文意思是投資回報率)。但UI自動化相比接口自動化、白盒測試等,它更貼近手工業務測試行為。對于剛起步測試左移、效率提升的團隊來說,是最迅速的切入點,也是廣大黑盒tester,提升自身技術能力的起跑線。
筆者接觸UI自動化一年多,兼顧業務測試的同時斷斷續續地投入,曾經無數次的想放棄:
“才剛寫完用例,怎么開發大哥又改了UI了?”
“維護這些破用例的時間,都夠我手工測三遍了,真的有意義么?”
“測試框架自己有bug,我改用例也沒用啊……”
“我調試的時候這個用例還是通的,放到daily里面跑就不通,到底怎么回事嘛!”
“adb怎么這么不穩定啊,老是斷?。?!”
“怎么跑著跑著就crash了,到底是被測應用有問題,還是測試代碼有問題???”
“明明界面上有這個元素,怎么就是查不到呢?”
“這破手機,能不能別老是系統彈框……”
“這手機真是渣,adb screencap截個圖,居然要三分鐘才返回!”
“這些控件都沒有id,沒有text,層級還三天兩頭改,要我怎么查……”
“查了這么多論壇,怎么就沒有人遇到過類似的問題呢?”
……
這些問題讓筆者一度懷疑,UI自動化這個TOPIC,是不是根本沒用,只是tester為了漲薪,或者為了擺脫重復無聊的手工業務測試,而YY出來自我欺騙的。
筆者將以上所有的問題簡單分成三類:設計類,環境類,細節類。一個好的設計模式,能夠避免一部分問題;一套好的環境,可以讓我們從乏味的維護工作中解脫;精益求精的細節,讓測試用例更加可靠穩定。
圖一 UI自動化常見問題
填掉這三類坑,基本上就獲得了一套低成本高產出、少量維護、穩定可靠的UI自動化用例集。
“才剛寫完用例,怎么開發大哥又改了UI了?”
“測試框架自己有bug,我改用例也沒用啊……”
這類問題,我們需要從根上治。UI自動化開發,也應該是嚴謹的開發工作,它也需要設計模式,也是磨刀不誤砍柴工。這里的設計,主要包括選工具、框架分層等。很多前輩都分析過UI自動化各類工具的優缺點,對工具選用筆者不再贅述。主要依托uiautomator來介紹下筆者認為比較巧妙的用例框架設計。
無論你選擇appium、uiautomator、robotium還是espresso,剛入門時,看到的sample應該大致都是這樣的。
圖二uiautomator和espresso邏輯樣例
問題在哪里?這些sample過于簡單,都只教了我們UI自動化三元素:怎么查找元素、怎么操作元素、怎么校驗結果。如果我們按照大多數分享帖或GitHub sample來寫作自己的case。最后這種沒有任何設計模式的框架,肯定會面臨重構。拿上面的espresso來說:
假如action_save這個id開發改了,而你的用例集中,有30個步驟用例到了這個id,一個個去改,是不是要瘋?
不厭其煩的重復寫onView(withXX(xxx)).perform(click())這一長串,你不煩?
筆者是如何做的? 分層設計 和 PageObjects 模式。這兩個方法,基本解決了筆者遇到的圖一中所有的設計類問題。
圖三 框架設計建議
按照圖三進行分層設計后,得到如圖四的測試代碼包。
圖四 分層后的用例框架
PageObjects模式發源于selenium社區,它的目的是減少重復代碼,當開發修改UI時,測試只需在有限的位置修改代碼。如果大家想深入了解PageObjects,請參照如下wiki:
https://github.com/SeleniumHQ/selenium/wiki/PageObjects
http://blog.csdn.net/kittyboy0001/article/details/25219053
我們來看一下,現在手管首頁Page包中的代碼和頁面。
圖五 手管首頁Page層部分代碼
回憶一下上面的google提供的sample,再對比引入分層設計和PO模式前后的代碼,點擊圖五中的一鍵加速:
圖六 引入PO前后代碼對比
帶來的好處,當然不僅僅是業務用例代碼更清爽。
通過將查找和操作封裝到基礎層中,這部分代碼就具體業務無關了,即使拿到其他產品中也可以復用;
通過page層的分離,所有的與業務相關的id,text等都被限定在了page包中,哪怕開發改了UI,修改page包特定的頁面中對應的元素就好了。
對page包進行合理的業務拆分,比如將手管分成 MainPage(主頁),SoftwareManagerPage(軟件管理頁),WiFiManagerPage(WiFi管理頁)等,在開發改了某個具體業務的界面后,測試能夠迅速知道測試代碼需要改哪里。
除了整個框架的設計,有時候一些小問題也可以經過巧妙設計。比如資源混淆的問題。
圖七 資源混淆
如圖七,在手機管家的發布包中,用uiautomatorviewer dump下來發現,一鍵優化的button,其resource-id是o3,但其實開發coding時,定義的id顯然不會用這種沒有任何字面意義的代號,它在混淆之前叫optimize_button。
純黑盒的UI自動化,也許你會摒棄optimize_button,直接寫o3,但這樣顯然不夠科學,既帶來了嚴重的代碼可讀性問題,同時一旦版本迭代,混淆變了,o3也許就變成了o4?;蛘吣銜岄_發給你測試的包,不要混淆,但如果想用UI自動化測試已發布的apk呢?
=解決該問題,也得從PageObjects說起?;氐綀D五中OPTIMIZE_BTN的定義,這個靜態變量并未在page中初始化,只有一個@FindBy的注解。其實,在框架層驅動測試開始前,框架會先調用如下圖八所示的setAllField來初始化所有的page頁面。
如果被測應用未混淆資源,該方法只是將@FindBy中的值賦值給Field。
如果被測應用已混淆資源,該方法則會從mObfuscationMap(未貼出全部代碼,實際是解析一個開發提供的混淆表,以原始id為key,混淆id為value的HashMap)中讀出對應的id對應關系,將混淆后的id賦值給Field。
圖八 Page層動態初始化
“adb怎么這么不穩定啊,老是斷?。?!”
“明明界面上有這個元素,怎么就是查不到呢?”
“這破手機,能不能別老是系統彈框……”
“這手機真是渣,adb screencap截個圖,居然要三分鐘才返回!”……
引子中提到的這些問題,根據經驗,多半你的環境執行環境還不夠穩定。
已知的ADB不穩定原因如:電壓不穩,各類手機助手的干擾,系統版本與ADB版本不匹配、ADBcrash等等。如果我們迎難而上,去重寫ADB,投入將無限擴大。所以建議主要的解決方案,還是盡量規避。
a) 選用可靠硬件規避電壓不穩定,github上的STF項目組有過成熟的經驗,選用性能更優的USB分接器,電壓和可靠性會有更穩定的表現。(附上鏈接,wiki Recommended hardware一節中有不同硬件詳細的性能對比, https://github.com/openstf/stf )
b) 屏蔽各類手機助手的干擾。91助手、豌豆莢等,基本都在adb上做了二次開發,它們會與原生adb間有兼容性問題。建議直接使用Linux/MAC系統作為運行環境以屏蔽這類干擾。
c) 降低用例在執行過程中對環境的依賴。Appium這類自動化工具,每一個測試步驟都需要PC端的appium server和測試手機端的bootstrap交互消息。測試過程中只要USB連接不穩定,都會導致整個測試套的失敗。所以筆者認為,使用更原生的uiautomator會是更好的選擇;同時,測試過程中的日志、截圖等,也盡量在測試手機上做持久化。
權限彈框,是手管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%。
自動化相比手工,它只會關注code告訴它的驗證點,所以選擇邏輯在用例中應該是禁用的。如下圖十三中右側的case,如果用例執行到if中,也許else流程中存在BUG,反之亦然。此時考慮拆分用例,左側才是理想的用例邏輯。
圖十三用例邏輯
另外,寫作case時,一定要牢記,只有我們告知程序要assert,它才會去assert。查找,操作,斷言,UI自動化三要素缺一不可。
在testng中,會提供dependsOnMethods注解,似乎在鼓勵寫作用例時,使用用例間依賴。但筆者認為,用例間的依賴,會帶來不必要的維護成本。只有高度解耦的用例邏輯,才能夠更加健壯的支撐用例執行順序調整、用例增刪、出現異常場景后,A用例失敗不會導致B用例也失敗。
有時候會遇到以下場景,雖然原生的自動化工具提供了等待元素可見的方法,但使用起來,還是無法真正等到元素可見。針對這個問題,如下圖的waitCondition方法是一個不錯的方案,它相對于thread.sleep來說,更節省時間。
圖十四反復等待方法
絕對坐標點擊,在不同尺寸屏幕上無法兼容。
第一方案應該是,推動開發對需要用到的控件添加ID或Accessibility。但根據經驗還是會有一些場景需要用到坐標點擊:
1, 考慮投入產出比,為所有控件添加id的成本過高;
2, 動態布局添加的ID都一樣;
3, 存在非xml布局的界面(代碼中直接布局)。
這時,筆者依然不建議mDevice.click(100,200)這樣的坐標點擊。有以下兩種值得一試的方案。
a) 找到相鄰控件坐標,計算當前控件的絕對坐標。如下圖十四,uiautomatorviewer中點擊右上角警告小三角,會得到有一些元素(黃色控件),是可能無法找到的。而使用相對坐標就是說,我們可以獲取它相鄰控件的坐標,然后減去或加上一個比較小的px值,再點擊計算后的坐標即可。
圖十五 相對坐標
b) 使用屏幕尺寸計算相對位置。在測試開始,將屏幕尺寸存下來,使用百分比的方式計算得到需要點擊的位置。如下,點擊【50%寬度,80%高度】的位置。
UI自動化測試是一門學起來很簡單,用起來很麻煩的測試技術。
想要入門,兩周就可以了解清楚uiautomator或espresso這類工具。UI自動化,無非就是查找元素、操作元素或設備、驗證結果。這三個步驟循環多次,就是一個用例。
但要用好,并產出能效,需要走的路其實很長。由于篇幅限制和知識有限,這里不可能把所有的問題一一列出。對于所有這些問題,無非兩個思路:一是繞過,二是解決。
選一個盡量簡化,盡量底層的工具(uiautomator或espresso),從根上繞過一些工具會存在的問題;
采用良好的設計模式,讓自己的框架更穩定,生命周期更長,維護成本更低;
明知道會耗費很多時間精力,收效卻很小的環境問題,盡量繞過;
優化用例邏輯和細節,使之穩定可靠,更能說服別人相信自動化的測試結論。
最后,祝愿大家在UI自動化的道路上越走越順!
原文轉自:https://cloud.tencent.com/community/article/212223