UIAutomation的功能测试代码是用Javascript编写的。通过标签和值的访问性来获得UI元素,同时完成相应的交互操作。通过一个实际的测试项目来手把手学习如何使用UIAutomation进行自动化测试。
前期准备:
第一个UIAutomation测试用例
var target = UIATarget.localTarget();
var app = target.frontMostApp();
var window = app.mainWindow();
target.logElementTree();
这样完成了我们的第一个UIAutomation测试用例。
2. 访问和操作用户界面元素
如果一个控件的Accessibility 是可以被访问的,你就可以设置和读取它的值。通过Interface Builder中,设置 Accessibility 为 Enabled。
理解元素的层级结构
在元素层级顶层是UIATarget 类,为了测试你的应用程序必须是前台活跃的程序,代码如下:
UIATarget.localTarget().frontMostApp();
为了获得应用程序主窗口,代码如下:
UIATarget.localTarget().frontMostApp().mainWindow();
通过从0开始的索引来访问文本框,代码如下:
var textField = UIATarget.localTarget().frontMostApp().mainWindow().textFields()[0];
通过这个元素的名称来访问文本框,代码如下:
var textField = UIATarget.localTarget().frontMostApp().mainWindow().textFields()["User Text"];
可以在Interface Builder中设置 UIAElement 的name属性,或通过代码方式更改
myTextField.accessibilityEnabled = YES;
myTextField.accessibilityLabel = @"User Text";
使用logElementTree 方法显示每个元素的所有子元素集。
UIALogger.logStart("Logging element tree ...");
UIATarget.localTarget().logElementTree();
UIALogger.logPass();
该命令的输出被Automation instrument 工具捕获并日志输出,如图:
3. 执行用户界面手势
点击(Tapping)
var tabBar = UIATarget.localTarget().frontMostApp().tabBar();
var tabButton = tabBar.buttons()["First"];
// Tap the tab bar
tabButton.tap();
可以通过屏幕的坐标,进行点击。代码如下:
UIATarget.localTarget().tap({x:100, y:200});
UIATarget.localTarget().doubleTap({x:100, y:200});
UIATarget.localTarget().twoFingerTap({x:100, y:200});
缩放(Pinching)需要指定开始和结束坐标,指定手势需要执行的时间,代码如下:
UIATarget.localTarget().pinchOpenFromToForDuration({x:20, y:200},{x:300, y:200},2);
UIATarget.localTarget().pinchCloseFromToForDuration({x:20, y:200}, {x:300, y:200},2);
拖拽 (Dragging)如:滚动一个列表或移动一个元素,代码如下:
UIATarget.localTarget().dragFromToForDuration({x:160, y:200},{x:160,y:400},1);
划动 (Flicking)比较快的拖拽,代码如下:
UIATarget.localTarget().flickFromTo({x:160, y:200},{x:160, y:400});
输入文字
var recipeName = "Unusually Long Name for a Recipe";
UITarget.localTarget().frontMostApp().mainWindow().textFields()[0].setValue(recipeName);
导航标签
var tabBar = UIATarget.localTarget().frontMostApp().mainWindow().tabBar();
var selectedTabName = tabBar.selectedButton().name();
if(selectedTabName != "First"){
tabBar.buttons()["First"].tap();
}
滚动到某个元素位置 可以滚动到某个不知道确切名字的位置
UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0].scrollToElementWithPredicate("name beginswith ‘ABC’");
添加超时周期 在此周期内它会在失效之前重复的尝试执行指定的动作,动作在未超时之前返回,你的脚本可以处理它。默认周期5秒,可以通过推入一个自定义的超时周期到栈顶。代码如下:
UIATarget.localTarget().pushTimeout(2);
UIATarget.localTarget().popTimeout();
UIATarget.localTarget().delay(2);
访问键盘上的按钮
app.keyboard().buttons()["Return"].tap();
输出测试结果
var testName = "Module 001 Test";
UIALogger.logStart(testName);
//some test code
UIALogger.logPass(testName);
增加截屏及日志输出
var testName = "Module 001 Test";
UIALogger.logStart(testName);
//some test code
UIALogger.logMessage("Starting Module 001 branch 2, validating input.");
//capture a screenshot with a specified name
UIATarget.localTarget().captureScreenWithName("SS001-2_AddedIngredient");
//more test code
UIALogger.logPass(testName);
验证测试结果
if (cell.isValid()) {
UIALogger.logPass(testName);
}
else {
UIALogger.logFail(testName);
}
处理非预期的提示框,在写自动化测试过程中,处理提示框是很难的一件事情:你已经很认真的写好了你的测试用例,然后在你准备睡觉之前将它跑起来,然后,到第二天早上,你发现你的测试用例被一个未知消息提示框给毁了。
UIATarget.onAlert = function onAlert(alert) {
var title = alert.name();
UIALogger.logWarning("Alert with title '" + title + "' encountered.");
// return false to use the default handler
return false;
}
处理预期的提示框
UIATarget.onAlert = function onAlert(alert) {
var title = alert.name();
UIALogger.logWarning("Alert with title '" + title + "' encountered.");
if (title == "The Alert We Expected") {
alert.buttons()["Continue"].tap();
return true; //alert handled, so bypass the default handler
}
// return false to use the default handler
return false;
}
旋转屏幕方向
var target = UIATarget.localTarget();
var app = target.frontMostApp();
//set orientation to landscape left
target.setDeviceOrientation(UIA_DEVICE_ORIENTATION_LANDSCAPELEFT);
UIALogger.logMessage("Current orientation now " + app.interfaceOrientation());
//reset orientation to portrait
target.setDeviceOrientation(UIA_DEVICE_ORIENTATION_PORTRAIT);
UIALogger.logMessage("Current orientation now " + app.interfaceOrientation());
多任务场景 当要测试一些多应用交互的场景时,需要将程序切到后台,等待一段时间后再返回活动状态,通过以下代码,可以模拟用户点击Home按钮。
UIATarget.localTarget().deactivateAppForDuration(10);
完整测试用例脚本
1. 在Scripts窗口里, 移除当前的脚本
2. 点击“Add > Import”然后选择TestAutomation/TestUI/Test-1.js(将下面的代码保存到这个路径)
3. 点击录制按钮 (⌘R)
下面是Test-1.js代码:
var testName = "Test 1";
var target = UIATarget.localTarget();
var app = target.frontMostApp();
var window = app.mainWindow();
UIALogger.logStart( testName );
app.logElementTree();
//-- select the elements
UIALogger.logMessage( "Select the first tab" );
var tabBar = app.tabBar();
var selectedTabName = tabBar.selectedButton().name();
if (selectedTabName != "First") {
tabBar.buttons()["First"].tap();
}
//-- tap on the text fiels
UIALogger.logMessage( "Tap on the text field now" );
var recipeName = "Unusually Long Name for a Recipe";
window.textFields()[0].setValue(recipeName);
target.delay( 2 );
//-- tap on the text fiels
UIALogger.logMessage( "Dismiss the keyboard" );
app.logElementTree();
app.keyboard().buttons()["Return"].tap();
target.deactivateAppForDuration(5);
target.setDeviceOrientation(UIA_DEVICE_ORIENTATION_LANDSCAPELEFT);
UIALogger.logMessage("Current orientation is " + app.interfaceOrientation());
target.delay(1);
target.setDeviceOrientation(UIA_DEVICE_ORIENTATION_PORTRAIT);
UIALogger.logMessage("Current orientation is " + app.interfaceOrientation());
target.delay(1);
var textValue = window.staticTexts()["RecipeName"].value();
if (textValue === recipeName){
UIALogger.logPass( testName );
}
else{
UIALogger.logFail( testName );
}
这段脚本先启动待测程序,然后,如果第一个tab没有被选的话就切换到第一个tab,并将上面的文本框的值设成“Unusually Long Name for a Recipe”,接着收起虚拟键盘。UIALogger的logMessage( String message) 方法用来将你想打印的信息输出到日志上去,UIALogger的logPass(String message)方法指明你的测试脚本已经成功的完成测试了。