本文中對(duì)于我來說,最大的學(xué)習(xí)點(diǎn)就是通過命令行運(yùn)行UI Automation,這對(duì)于我來說,就好像打開一個(gè)新的學(xué)習(xí)窗口。
-------------------------------------原文作者文分割線-------------------------------------
秉著想偷懶的原則和測(cè)試這塊一直存在的詬病,空閑的時(shí)把蘋果提供的UIAutomation研究了一番,心想這樣就可以坐等APP自己跑完所有流程然后輸出 carsh 報(bào)告。但是想象很豐滿,現(xiàn)實(shí)很骨
感,UiAutomation 并沒有想象中那么的完美。
基本介紹
? + I 打開Instruments,選擇 UiAutomation,基本界面就是這樣
功能區(qū)域介紹:① 開始、結(jié)束測(cè)試按鈕,選擇設(shè)備和項(xiàng)目菜單② JS腳本編輯區(qū),Trace Log 和 Editor Log顯示區(qū)③ 自動(dòng)化測(cè)試時(shí)間線④ 其他菜單頁面⑤ 腳本或是Log選擇菜單⑥ 自動(dòng)生成JS腳本代碼的開始、結(jié)束和停止按鈕
看一個(gè)簡單的測(cè)試?yán)?,APP的界面上有一個(gè)UITextField,稱之為A元素,Navigationbar有個(gè)rightItem,稱之為B元素,點(diǎn)擊rightItem會(huì)push到另一個(gè)VC
在④中選擇一個(gè)空的腳本寫入下列代碼,對(duì) JS 不了解的也不用當(dāng)心,自動(dòng)化測(cè)試的 JS 代碼非常簡單。
var target = UIATarget.localTarget();
var app = target.frontMostApp();
var window = app.mainWindow();
// 打印元素樹 app.logElementTree();
? + R跑一下,②區(qū)域應(yīng)該會(huì)自動(dòng)切換到Log界面
Log打印出來的是當(dāng)前界面的元素(UIAElement)樹,同層級(jí)的元素會(huì)被包含到數(shù)組中,模擬用戶操作其實(shí)就是對(duì)元素的操作,那么獲取到元素才是關(guān)鍵。logElementTree()這個(gè)函數(shù)非常有用,在頁面切換的時(shí)候記得要再次調(diào)用,以便找到你想要的元素
**補(bǔ)充腳本:
**
var target = UIATarget.localTarget();
var app = target.frontMostApp();
var window = app.mainWindow();
app.logElementTree();
var textField = window.textFields()[0];
textField.setValue("122");// 在 A 元素中輸入122
target.delay(1); // 停頓1秒
var rightButton = window.navigationBar().buttons()['Button'];// 獲取 B 元素
rightButton.tap(); // 點(diǎn)擊 B 元素
運(yùn)行后的效果是:A 元素被輸入了122的字符——>然后 B 元素被點(diǎn)擊——>APP 進(jìn)入了一個(gè)頁面
**代碼解讀:
**
獲取 A 元素:window.textFields()[0],界面上只有一個(gè)textField,那么當(dāng)然是textFields數(shù)組的第一個(gè)元素
獲取 B 元素:window.navigationBar().buttons()['Button'],由于 B 元素是在navigationBar上,所以需要先獲取navigationBar,再從buttons數(shù)組中獲取 B 元素,通過 B 元素name屬性的值Button(默認(rèn)值)獲取。
元素的name值也可以手動(dòng)設(shè)置,比如設(shè)置 A 元素的name值為textField,注意:不要設(shè)置元素的可訪問性(isAccessibilityElement)為NO
或者用代碼設(shè)置
self.aView.accessibilityLabel = @"textField";
那么就可以通過下面這種方式獲取
var textField = window.textFields()['textField'];
關(guān)于元素的可執(zhí)行方法,比如tap(),可以查看蘋果的官方文檔。
**以及元素?cái)?shù)組包括:
**
- buttons() ;
- images() ;
- scrollViews() ;
- textFields() ;
- webViews() ;
- segmentedControls() ;
- sliders() ;
- staticTexts() ;
- switches() ;
- tabBar() ;
- tableViews() ;
- textViews() ;
- toolbar() ;
- toolbars() ;
- secureTextFields(); // 加密的UITextField...
蘋果另外也提供一個(gè)輔助檢查功能工具,可以方便查看元素的信息打開設(shè)置(Settings)-- 通用(General)-- 輔助功能(Accessibility)-- 輔助功能檢查器(Accessibility Inspector)
操作腳本錄制
在④中創(chuàng)建一個(gè)新的腳本,在⑥中點(diǎn)擊中間的紅色按鈕,代表開始錄制用戶操作并轉(zhuǎn)換為JS代碼,點(diǎn)擊右側(cè)的按鈕,停止錄制,點(diǎn)擊左側(cè)按鈕,執(zhí)行腳本。這時(shí)候你肯定在想,既然有這功能,還需要寫什么腳本,真是這樣嗎?下面錄制的一個(gè)同樣的操作:點(diǎn)擊 A 元素——>在鍵盤上輸入112——>點(diǎn)擊 B 元素
var target = UIATarget.localTarget();
target.frontMostApp().mainWindow().textFields()[0].textFields()[0].tap();
target.frontMostApp().keyboard().typeString("112");
target.frontMostApp().navigationBar().buttons()["Button"].tap();
這種方式生成的代碼會(huì)有個(gè)問題,在各個(gè)操作之間都不會(huì)生成停頓代碼,也就是target.delay();。如果對(duì)于那種夸頁面的復(fù)雜操作,這種方式錄制的腳本,在重新執(zhí)行的時(shí)候有可能會(huì)報(bào)錯(cuò)。
舉個(gè)例子:點(diǎn)擊一個(gè)按鈕后present到另一個(gè)頁面,然后在另一個(gè)界面上點(diǎn)擊上面的textfield,那么大概的腳本應(yīng)該是這樣:
var target = UIATarget.localTarget();
target.frontMostApp().mainWindow().buttons()[0].tap();
target.frontMostApp().mainWindow().textFields()[0].tap();
當(dāng)執(zhí)行到target.frontMostApp().mainWindow().textFields()[0].tap();這時(shí)候界面其實(shí)還處于前一個(gè),那么獲取textfield必然會(huì)有問題,所以如果遇到此類問題,不妨各個(gè)操作之間加入target.delay();試試。另外,缺少邏輯判斷,比如當(dāng)情況一點(diǎn)擊這個(gè)按鈕,情況二點(diǎn)擊另一個(gè)。
所以想完全依靠這種方式,偷懶不用寫腳本,基本上行不通。
常用操作
蘋果官方API文檔
屏幕點(diǎn)擊
// 單擊 UIATarget.localTarget().tap({x:100, y:200});
// 雙擊 UIATarget.localTarget().doubleTap({x:100, y:200});
// 雙指點(diǎn)擊 UIATarget.localTarget().twoFing**ap({x:100, y:200});
縮放
// 放大 UIATarget.localTarget().pinchOpenFromToForDuration({x:20, y:200},{x:300, y:200},2);
// 縮小 UIATarget.localTarget().pinchCloseFromToForDuration({x:20, y:200}, {x:300, y:200},2);
拖拽與劃動(dòng)
// 拖拽 UIATarget.localTarget().pinchOpenFromToForDuration({x:20, y:200},{x:300, y:200},2);
// 劃動(dòng) UIATarget.localTarget().pinchCloseFromToForDuration({x:20, y:200}, {x:300, y:200},2);
//打印
UIALogger.logStart("xxx");
UIALogger.logFail("xxx");
UIALogger.logDebug("xxx");
UIALogger.logMessage("xxx");
命令行執(zhí)行
使用命令行的原因是,Instruments工具跑測(cè)試實(shí)在太慢了,命令如下
instruments -t /Applications/Xcode.app/Contents/Applications/Instruments.app/Contents/PlugIns/AutomationInstrument.xrplugin/Contents/Resources/Automation.tracetemplate -w "iPhone 5s (8.3 Simulator)" /Users/xxxx/Library/Developer/CoreSimulator/Devices/A35F991E-425E-4F41-B76C-B7C176A06C36/data/Containers/Data/Application/324E3D2F-A0BC-4E96-97DF-97E791AB10A8/xxxx.app -e UIASCRIPT /Users/xxxx/Desktop/untitled.js -e UIARESULTSPATH /Users/xxxx/Desktop/tmp/
需要自行修改的部分
// 測(cè)試設(shè)備 -w "iPhone 5s (8.3 Simulator)"
// 項(xiàng)目目錄,如果目錄中沒有xxxx.app也沒關(guān)系
/Users/xxxx/Library/Developer/CoreSimulator/Devices/A35F991E-425E-4F41-B76C-B7C176A06C36/data/Containers/Data/Application/324E3D2F-A0BC-4E96-97DF-97E791AB10A8/xxxx.app
// 腳本目錄 -e UIASCRIPT /Users/xxxx/Desktop/untitled.js
// 測(cè)試報(bào)告輸出目錄-e UIARESULTSPATH /Users/xxxx/Desktop/tmp/
最后之所以說 UiAutomation 并沒有想象中那么的完美的原因有下面幾點(diǎn):
1、JS腳本調(diào)試較為麻煩 ;
2、UiAutomation 目前存在不少bug,比如手動(dòng)設(shè)置accessibilityLabel無效、莫名其妙的報(bào)錯(cuò),再跑一次又好了 ;
3、不夠完善,比如不能判斷一個(gè)button是否可用(對(duì)應(yīng)enabled屬性),UIAlterView無法手動(dòng)處理(網(wǎng)上提供的方法都無效),多份JS腳本,不能設(shè)置完成之后自動(dòng)跑下一份 ;
4、輸出的測(cè)試報(bào)告比較簡單,只有簡單的log和截圖,截圖的規(guī)則也不太清楚(可能有提供截圖的API) ;
轉(zhuǎn)自:《UI自動(dòng)化測(cè)試》