摘抄自:http://tmq.qq.com/2017/03/uizidonghua/
小心底洗!做UI自動(dòng)化一定要跨過這些坑
一涩澡、引子
UI自動(dòng)化祝懂,在移動(dòng)互聯(lián)網(wǎng)時(shí)代的今天票摇,一直都是在各大測(cè)試測(cè)試社區(qū)最為火爆的一個(gè)TOPIC。甚至在測(cè)試同行面前一提起自動(dòng)化砚蓬,大家就會(huì)自然而然的問:“恩矢门,你們是用的什么框架?appium灰蛙?還是robotium祟剔?”
其實(shí)在筆者看來,UI自動(dòng)化是一個(gè)ROI較低的測(cè)試項(xiàng)(ROI即return?on?investment摩梧,中文意思是投資回報(bào)率)物延。但UI自動(dòng)化相比接口自動(dòng)化、白盒測(cè)試等仅父,它更貼近手工業(yè)務(wù)測(cè)試行為叛薯。對(duì)于剛起步測(cè)試左移浑吟、效率提升的團(tuán)隊(duì)來說,是最迅速的切入點(diǎn)耗溜,也是廣大黑盒tester组力,提升自身技術(shù)能力的起跑線。筆者接觸UI自動(dòng)化一年多抖拴,兼顧業(yè)務(wù)測(cè)試的同時(shí)斷斷續(xù)續(xù)地投入燎字,曾經(jīng)無數(shù)次的想放棄:“才剛寫完用例,怎么開發(fā)大哥又改了UI了城舞?”
“維護(hù)這些破用例的時(shí)間轩触,都?jí)蛭沂止y(cè)三遍了,真的有意義么家夺?”
“測(cè)試框架自己有bug脱柱,我改用例也沒用啊……”
“我調(diào)試的時(shí)候這個(gè)用例還是通的,放到daily里面跑就不通拉馋,到底怎么回事嘛榨为!”
“adb怎么這么不穩(wěn)定啊,老是斷;蛙睢K婀搿!”
“怎么跑著跑著就crash了蔓腐,到底是被測(cè)應(yīng)用有問題矩乐,還是測(cè)試代碼有問題啊回论?”
“明明界面上有這個(gè)元素散罕,怎么就是查不到呢?”
“這破手機(jī)傀蓉,能不能別老是系統(tǒng)彈框……”
“這手機(jī)真是渣欧漱,adb screencap截個(gè)圖,居然要三分鐘才返回葬燎!”
“這些控件都沒有id误甚,沒有text,層級(jí)還三天兩頭改谱净,要我怎么查……”
“查了這么多論壇窑邦,怎么就沒有人遇到過類似的問題呢?”
……
這些問題讓筆者一度懷疑壕探,UI自動(dòng)化這個(gè)TOPIC奕翔,是不是根本沒用,只是tester為了漲薪浩蓉,或者為了擺脫重復(fù)無聊的手工業(yè)務(wù)測(cè)試派继,而YY出來自我欺騙的。
二捻艳、問題分類及目標(biāo)明確
筆者將以上所有的問題簡(jiǎn)單分成三類:設(shè)計(jì)類驾窟、環(huán)境類、細(xì)節(jié)類认轨。一個(gè)好的設(shè)計(jì)模式绅络,能夠避免一部分問題;一套好的環(huán)境嘁字,可以讓我們從乏味的維護(hù)工作中解脫恩急;精益求精的細(xì)節(jié),讓測(cè)試用例更加可靠穩(wěn)定纪蜒。
圖一UI自動(dòng)化常見問題
填掉這三類坑衷恭,基本上就獲得了一套低成本高產(chǎn)出、少量維護(hù)纯续、穩(wěn)定可靠的UI自動(dòng)化用例集随珠。
三、設(shè)計(jì)類問題分析與解決
“才剛寫完用例猬错,怎么開發(fā)大哥又改了UI了窗看?”
“測(cè)試框架自己有bug,我改用例也沒用啊……”這類問題倦炒,我們需要從根上治显沈。UI自動(dòng)化開發(fā),也應(yīng)該是嚴(yán)謹(jǐn)?shù)拈_發(fā)工作逢唤,它也需要設(shè)計(jì)模式拉讯,也是磨刀不誤砍柴工。這里的設(shè)計(jì)智玻,主要包括選工具遂唧、框架分層等。很多前輩都分析過UI自動(dòng)化各類工具的優(yōu)缺點(diǎn)吊奢,對(duì)工具選用筆者不再贅述盖彭。主要依托uiautomator來介紹下筆者認(rèn)為比較巧妙的用例框架設(shè)計(jì)。
1页滚、優(yōu)化測(cè)試代碼框架
無論你選擇appium召边、uiautomator、robotium還是espresso裹驰,剛?cè)腴T時(shí)隧熙,看到的sample應(yīng)該大致都是這樣的。
圖二 uiautomator和espresso邏輯樣例
問題在哪里幻林?這些sample過于簡(jiǎn)單贞盯,都只教了我們UI自動(dòng)化三元素:怎么查找元素音念、怎么操作元素、怎么校驗(yàn)結(jié)果躏敢。如果我們按照大多數(shù)分享帖或GitHub sample來寫作自己的case闷愤。最后這種沒有任何設(shè)計(jì)模式的框架,肯定會(huì)面臨重構(gòu)件余。拿上面的espresso來說:
1.假如action_save這個(gè)id開發(fā)改了讥脐,而你的用例集中,有30個(gè)步驟用例到了這個(gè)id啼器,一個(gè)個(gè)去改旬渠,是不是要瘋?
2.不厭其煩的重復(fù)寫onView(withXX(xxx)).perform(click())這一長(zhǎng)串端壳,你不煩告丢?
筆者是如何做的呢?
分層設(shè)計(jì)和PageObjects模式更哄。這兩個(gè)方法芋齿,基本解決了筆者遇到的圖一中所有的設(shè)計(jì)類問題。
圖三 框架設(shè)計(jì)建議
按照?qǐng)D三進(jìn)行分層設(shè)計(jì)后成翩,得到如圖四的測(cè)試代碼包觅捆。
圖四 分層后的用例框架
PageObjects模式發(fā)源于selenium社區(qū),它的目的是減少重復(fù)代碼麻敌,當(dāng)開發(fā)修改UI時(shí)栅炒,測(cè)試只需在有限的位置修改代碼。如果大家想深入了解PageObjects术羔,請(qǐng)參照如下wiki:
(https://github.com/SeleniumHQ/selenium/wiki/PageObjects
http://blog.csdn.net/kittyboy0001/article/details/25219053)赢赊。
我們來看一下,現(xiàn)在手管首頁P(yáng)age包中的代碼和頁面级历。
圖五 手管首頁P(yáng)age層部分代碼
回憶一下上面的google提供的sample释移,再對(duì)比引入分層設(shè)計(jì)和PO模式前后的代碼,點(diǎn)擊圖五中的一鍵加速:
圖六 引入PO前后代碼對(duì)比
帶來的好處寥殖,當(dāng)然不僅僅是業(yè)務(wù)用例代碼更清爽玩讳。
1、通過將查找和操作封裝到基礎(chǔ)層中嚼贡,這部分代碼就具體業(yè)務(wù)無關(guān)了熏纯,即使拿到其他產(chǎn)品中也可以復(fù)用;
2粤策、通過page層的分離樟澜,所有的與業(yè)務(wù)相關(guān)的id,text等都被限定在了page包中,哪怕開發(fā)改了UI秩贰,修改page包特定的頁面中對(duì)應(yīng)的元素就好了霹俺。
3、對(duì)page包進(jìn)行合理的業(yè)務(wù)拆分萍膛,比如將手管分成 MainPage(主頁)吭服,SoftwareManagerPage(軟件管理頁),WiFiManagerPage(WiFi管理頁)等蝗罗,在開發(fā)改了某個(gè)具體業(yè)務(wù)的界面后,測(cè)試能夠迅速知道測(cè)試代碼需要改哪里蝌戒。
2串塑、兼容資源混淆的測(cè)試代碼
除了整個(gè)框架的設(shè)計(jì),有時(shí)候一些小問題也可以經(jīng)過巧妙設(shè)計(jì)北苟。比如資源混淆的問題桩匪。
圖七 資源混淆
如圖七,在手機(jī)管家的發(fā)布包中友鼻,用uiautomatorviewer dump下來發(fā)現(xiàn)傻昙,一鍵優(yōu)化的button,其resource-id是o3彩扔,但其實(shí)開發(fā)coding時(shí)妆档,定義的id顯然不會(huì)用這種沒有任何字面意義的代號(hào),它在混淆之前叫optimize_button虫碉。
純黑盒的UI自動(dòng)化贾惦,也許你會(huì)摒棄optimize_button,直接寫o3敦捧,但這樣顯然不夠科學(xué)须板,既帶來了嚴(yán)重的代碼可讀性問題,同時(shí)一旦版本迭代兢卵,混淆變了习瑰,o3也許就變成了o4』嗷纾或者你會(huì)讓開發(fā)給你測(cè)試的包甜奄,不要混淆,但如果想用UI自動(dòng)化測(cè)試已發(fā)布的apk呢王滤?
解決該問題贺嫂,也得從PageObjects說起⊙阆纾回到圖五中OPTIMIZE_BTN的定義第喳,這個(gè)靜態(tài)變量并未在page中初始化,只有一個(gè)@FindBy的注解踱稍。其實(shí)曲饱,在框架層驅(qū)動(dòng)測(cè)試開始前悠抹,框架會(huì)先調(diào)用如下圖八所示的setAllField來初始化所有的page頁面。
1扩淀、如果被測(cè)應(yīng)用未混淆資源楔敌,該方法只是將@FindBy中的值賦值給Field。
2驻谆、如果被測(cè)應(yīng)用已混淆資源卵凑,該方法則會(huì)從mObfuscationMap(未貼出全部代碼,實(shí)際是解析一個(gè)開發(fā)提供的混淆表胜臊,以原始id為key勺卢,混淆id為value的HashMap)中讀出對(duì)應(yīng)的id對(duì)應(yīng)關(guān)系,將混淆后的id賦值給Field象对。
圖八 Page層動(dòng)態(tài)初始化
四黑忱、環(huán)境類問題分析與解決
“adb怎么這么不穩(wěn)定啊,老是斷@漳А8ι贰!”“明明界面上有這個(gè)元素冠绢,怎么就是查不到呢抚吠?”“這破手機(jī),能不能別老是系統(tǒng)彈框……”
“這手機(jī)真是渣唐全,adb screencap截個(gè)圖埃跷,居然要三分鐘才返回!”……
引子中提到的這些問題邮利,根據(jù)經(jīng)驗(yàn)弥雹,多半你的環(huán)境執(zhí)行環(huán)境還不夠穩(wěn)定。
1延届、ADB相關(guān)問題
已知的ADB不穩(wěn)定原因如:電壓不穩(wěn)剪勿,各類手機(jī)助手的干擾,系統(tǒng)版本與ADB版本不匹配方庭、ADB crash等等厕吉。如果我們迎難而上,去重寫ADB械念,投入將無限擴(kuò)大头朱。所以建議主要的解決方案,還是盡量規(guī)避龄减。
1)项钮、選用可靠硬件規(guī)避電壓不穩(wěn)定。github上的STF項(xiàng)目組有過成熟的經(jīng)驗(yàn),選用性能更優(yōu)的USB分接器烁巫,電壓和可靠性會(huì)有更穩(wěn)定的表現(xiàn)署隘。(附上鏈接,wiki Recommended hardware一節(jié)中有不同硬件詳細(xì)的性能對(duì)比:https://github.com/openstf/stf)亚隙。
2)磁餐、屏蔽各類手機(jī)助手的干擾。91助手阿弃、豌豆莢等诊霹,基本都在adb上做了二次開發(fā),它們會(huì)與原生adb間有兼容性問題渣淳。建議直接使用Linux/MAC系統(tǒng)作為運(yùn)行環(huán)境以屏蔽這類干擾畅哑。
3)、降低用例在執(zhí)行過程中對(duì)環(huán)境的依賴水由。Appium這類自動(dòng)化工具,每一個(gè)測(cè)試步驟都需要PC端的appium server和測(cè)試手機(jī)端的bootstrap交互消息赛蔫。測(cè)試過程中只要USB連接不穩(wěn)定砂客,都會(huì)導(dǎo)致整個(gè)測(cè)試套的失敗。所以筆者認(rèn)為呵恢,使用更原生的uiautomator會(huì)是更好的選擇鞠值;同時(shí),測(cè)試過程中的日志渗钉、截圖等彤恶,也盡量在測(cè)試手機(jī)上做持久化。
2鳄橘、彈框問題的解決
權(quán)限彈框声离,是手管UI自動(dòng)化中的一個(gè)大坑。如下圖瘫怜,是測(cè)試手管過程中术徊,在華為手機(jī)上遇到的部分權(quán)限彈框。這些彈框鲸湃,并不會(huì)用例每次執(zhí)行都彈出赠涮,不同廠商的彈出框也不一致。顯然點(diǎn)擊彈框的邏輯暗挑,寫在case邏輯中笋除,只會(huì)導(dǎo)致自動(dòng)化變得更復(fù)雜更不穩(wěn)定。
圖九 各類權(quán)限彈框
uiautomator的watcher炸裆,能夠完全實(shí)現(xiàn)點(diǎn)擊彈框和用例邏輯的解耦垃它。當(dāng)前筆者的實(shí)現(xiàn)邏輯是,監(jiān)聽彈框上的某個(gè)控件,當(dāng)該控件出現(xiàn)時(shí)嗤瞎,執(zhí)行action來點(diǎn)擊掉其中的取消或確定按鈕墙歪。這樣,用例就只需關(guān)注業(yè)務(wù)邏輯贝奇,而任何時(shí)候的彈框虹菲,都由watcher來自動(dòng)點(diǎn)擊。如下圖中掉瞳,checkForCondition關(guān)注條件毕源,action是操作。
圖十 查找型Watcher
將所有的watcher分不同的手機(jī)廠商進(jìn)行注冊(cè)后陕习,再調(diào)用runWatchers()霎褐,然后再執(zhí)行用例。該方法可以在@BeforeClass中或者RunListener的testRunStarted中調(diào)用该镣。當(dāng)然冻璃,如果某個(gè)用例不想某個(gè)具體的彈框被watcher點(diǎn)擊掉,也可以調(diào)用removeWatcher()反注冊(cè)损合。
圖十一 注冊(cè)監(jiān)聽器
Watcher并不能解決所有的彈框問題省艳。例如,在開啟WiFi的場(chǎng)景中嫁审,由于WifiManager的setWifiEnabled和UI上的彈框點(diǎn)擊是同步的(意思是調(diào)用了setWifiEnabled之后跋炕,如果界面上不點(diǎn)允許,該方法是不會(huì)返回的)律适,使用上面的watcher方式并不會(huì)點(diǎn)擊WiFi權(quán)限申請(qǐng)的允許辐烂。這時(shí),就需要用到線程方式來解決(如下圖十二)捂贿,調(diào)用setWifiEnabled前纠修,先啟動(dòng)一個(gè)線程等待彈框彈出。
圖十二 多線程方式點(diǎn)掉彈框
五眷蜓、細(xì)節(jié)類問題分析與解決
“我調(diào)試的時(shí)候這個(gè)用例還是通的分瘾,放到daily里面跑就不通,到底怎么回事嘛吁系!”
出現(xiàn)上述問題德召,多半是因?yàn)槲覀兊挠美?xì)節(jié)不夠嚴(yán)謹(jǐn)。這類問題汽纤,往往決定著我們自動(dòng)化用例集上岗,是不是能從90%的case通過率,提升到100%蕴坪。
1肴掷、順序邏輯的用例
自動(dòng)化相比手工敬锐,它只會(huì)關(guān)注code告訴它的驗(yàn)證點(diǎn),所以選擇邏輯在用例中應(yīng)該是禁用的呆瞻。如下圖十三中右側(cè)的case台夺,如果用例執(zhí)行到if中,也許else流程中存在BUG痴脾,反之亦然颤介。此時(shí)考慮拆分用例,左側(cè)才是理想的用例邏輯赞赖。
圖十三 用例邏輯
另外滚朵,寫作case時(shí),一定要牢記前域,只有我們告知程序要assert辕近,它才會(huì)去assert。查找匿垄,操作移宅,斷言,UI自動(dòng)化三要素缺一不可椿疗。
2吞杭、解耦的用例
在testng中,會(huì)提供dependsOnMethods注解变丧,似乎在鼓勵(lì)寫作用例時(shí),使用用例間依賴绢掰。但筆者認(rèn)為痒蓬,用例間的依賴,會(huì)帶來不必要的維護(hù)成本滴劲。只有高度解耦的用例邏輯攻晒,才能夠更加健壯的支撐用例執(zhí)行順序調(diào)整、用例增刪班挖、出現(xiàn)異常場(chǎng)景后鲁捏,A用例失敗不會(huì)導(dǎo)致B用例也失敗杠览。
3芬位、優(yōu)化等待
有時(shí)候會(huì)遇到以下場(chǎng)景襟己,雖然原生的自動(dòng)化工具提供了等待元素可見的方法寥枝,但使用起來醇滥,還是無法真正等到元素可見视事。針對(duì)這個(gè)問題至耻,如下圖的waitCondition方法是一個(gè)不錯(cuò)的方案镣典,它相對(duì)于thread.sleep來說渔期,更節(jié)省時(shí)間运吓。
圖十四 反復(fù)等待方法
4渴邦、不用絕對(duì)坐標(biāo)點(diǎn)擊
絕對(duì)坐標(biāo)點(diǎn)擊,在不同尺寸屏幕上無法兼容拘哨。
第一方案應(yīng)該是谋梭,推動(dòng)開發(fā)對(duì)需要用到的控件添加ID或Accessibility。但根據(jù)經(jīng)驗(yàn)還是會(huì)有一些場(chǎng)景需要用到坐標(biāo)點(diǎn)擊:
1倦青、考慮投入產(chǎn)出比瓮床,為所有控件添加id的成本過高;
2姨夹、動(dòng)態(tài)布局添加的ID都一樣纤垂;
3、存在非xml布局的界面(代碼中直接布局)磷账。
這時(shí)峭沦,筆者依然不建議mDevice.click(100,200)這樣的坐標(biāo)點(diǎn)擊。有以下兩種值得一試的方案逃糟。
1吼鱼、找到相鄰控件坐標(biāo),計(jì)算當(dāng)前控件的絕對(duì)坐標(biāo)绰咽。如下圖十四菇肃,uiautomatorviewer中點(diǎn)擊右上角警告小三角,會(huì)得到有一些元素(黃色控件)取募,是可能無法找到的琐谤。而使用相對(duì)坐標(biāo)就是說,我們可以獲取它相鄰控件的坐標(biāo)玩敏,然后減去或加上一個(gè)比較小的px值斗忌,再點(diǎn)擊計(jì)算后的坐標(biāo)即可。
圖十五相對(duì)坐標(biāo)
2旺聚、使用屏幕尺寸計(jì)算相對(duì)位置织阳。在測(cè)試開始,將屏幕尺寸存下來砰粹,使用百分比的方式計(jì)算得到需要點(diǎn)擊的位置唧躲。如下代碼,點(diǎn)擊【50%屏幕寬度碱璃,80%屏幕高度】的位置弄痹。
CODE: mDevice.click( screenWidth/2 , screenHeight*80/100 )。
六嵌器、總結(jié)
UI自動(dòng)化測(cè)試是一門學(xué)起來很簡(jiǎn)單界酒,用起來很麻煩的測(cè)試技術(shù)。
想要入門嘴秸,兩周就可以了解清楚uiautomator或espresso這類工具毁欣。UI自動(dòng)化庇谆,無非就是查找元素、操作元素或設(shè)備凭疮、驗(yàn)證結(jié)果饭耳。這三個(gè)步驟循環(huán)多次,就是一個(gè)用例执解。但要用好寞肖,并產(chǎn)出能效,需要走的路其實(shí)很長(zhǎng)衰腌。由于篇幅限制和知識(shí)有限新蟆,這里不可能把所有的問題一一列出。對(duì)于所有這些問題右蕊,無非兩個(gè)思路:一是繞過琼稻,二是解決。1饶囚、選一個(gè)盡量簡(jiǎn)化帕翻,盡量底層的工具(uiautomator或espresso),從根上繞過一些工具會(huì)存在的問題;
2萝风、采用良好的設(shè)計(jì)模式嘀掸,讓自己的框架更穩(wěn)定,生命周期更長(zhǎng)规惰,維護(hù)成本更低睬塌;
3、明知道會(huì)耗費(fèi)很多時(shí)間精力歇万,收效卻很小的環(huán)境問題衫仑,盡量繞過;
4堕花、優(yōu)化用例邏輯和細(xì)節(jié),使之穩(wěn)定可靠粥鞋,更能說服別人相信自動(dòng)化的測(cè)試結(jié)論缘挽。
七、最后
不知您是否也曾在UI自動(dòng)化過程中遇到過難以解決的問題呢呻粹?歡迎大家留言討論壕曼。也祝愿大家在UI自動(dòng)化的道路上越走越順!