首页 > 代码库 > ios 性能优化之自动化UI测试

ios 性能优化之自动化UI测试

自动化UI测试

来源:http://www.cnblogs.com/chensheng12330/p/3947588.html

 

使用自动化工具来自动化用户界面测试你的iOS应用程序通过测试脚本编写。 这些脚本模拟用户操作通过调用UI自动化、一个JavaScript编程接口,指定要执行的动作在你的应用程序运行。 在测试过程中,系统日志信息返回给你。

UI交互的自动化测试时,您免费其他工作的关键人员和资源。 这样你最小化程序错误,缩短开发产品更新所需的时间。

本章描述了如何使用仪器的自动化模板执行脚本。 它还描述了如何网与UI自动化脚本编程接口来验证应用程序可以执行以下操作:

  • 访问它的UI元素的层次结构

  • 添加时间灵活性通过超时时间

  • 日志和验证返回的信息工具

  • 妥善处理警报

  • 优雅地处理设备的变化方向

  • 处理多任务

当你完成这一章,寻找更多关于每个类的详细信息 UI自动化JavaScript参考 

自动化仪表的一个重要的好处是,您可以使用它与其他工具来执行复杂的测试,如跟踪内存泄漏和隔离性能问题的原因。

注意: 自动化仪器仅适用于应用程序代码与开发配置概要文件签署。 应用程序与分布配置概要文件签署不能与UI自动化自动化编程接口。

重要的是: 模拟操作可能不会阻止测试装置auto-locking。 在设备上运行测试之前,您应该将Auto-Lock偏好设置为从不。

编写自动化测试脚本

您的测试脚本必须是一个有效的可执行JavaScript文件访问主机上的仪器。 因为外面的脚本运行你的应用程序,您正在测试的应用程序版本可以你提交的应用程序商店。

因为每个应用程序都是不同的,通常为一个应用程序是一个脚本不能接受在另一个应用程序使用。在这种情况下,您可以创建自己的脚本里面的仪器。

bullet
创建一个脚本

创建一个脚本后,您想要使用它在你的应用程序的开发。这可以通过导入保存的脚本和自动化仪表运行它。 图十一 显示了一个使用脚本运行完成。

图十一 通过脚本运行iOS应用程序

测试自动化脚本

你在JavaScript编写自动化测试,使用UI自动化JavaScript库指定动作应该表现在你的应用程序运行。

您的测试脚本必须是一个有效的可执行JavaScript文件访问主机上的仪器。 外面运行你的应用程序,所以测试版本的应用程序可以是相同的版本,您提交到iTunes应用程序商店。

您可以创建尽可能多的脚本,但是一次只能运行一个。 这个API提供一个 #进口 指令,允许您编写更小、可重用的离散的测试脚本。 例如,如果你是定义在一个文件中常用的函数命名 TestUtilities.js 这些功能,你可以使用在您的测试脚本,包括脚本

#进口“< path-to-library-folder > / TestUtilities.js”

bullet
导入以前保存的脚本

自动化跟踪模板执行一个脚本,模拟用户界面交互的iOS应用程序启动仪器。 它只包括自动化仪表。

配置自动化仪表自动控制启动和停止脚本工具栏中的工具记录的按钮,选择复选框的运行记录。

如果你的应用程序崩溃或背景,您的脚本被阻塞,直到应用程序再次frontmost,此时脚本继续运行。

重要的是: 您必须显式地停止记录。 完成或终止您的脚本不关掉录音。

访问和操作UI元素

Accessibility-based机制UI自动化特性代表了每一个控制在你的应用程序作为一个独特的识别元素。 执行一个操作在你的应用程序的一个元素,您明确地识别该元素的应用的元素的层次结构。 为了充分理解这一节中,您应该熟悉的信息 iOS人机界面指南 

本节说明元素层次结构,指的是菜谱iOS应用程序所示 图成分 的代码示例 iPhoneCoreDataRecipes 从iOS开发中心。

图成分 菜谱应用(食谱屏幕)

UI元素的可访问性

每个访问元素从基本元素是遗传的, UIAElement 。 每个元素可以包含零个或多个其他元素。

如下详细,脚本可以访问单个元素在元素的位置层次结构。 不过,您可以通过设置每个元素分配一个唯一的名称标签属性并确保可访问性是选择界面构建器的控制为代表的元素,如所示 图取得 

图取得 设置可访问性标签界面构建器

UI自动化使用可访问性标签(如果它是集)获得一个名称为每个元素属性。 除了明显的好处,使用这样的名字可以极大地简化您的测试脚本的开发和维护。

四个属性的名称属性是这些元素,可以在您的测试脚本中非常有用。

  • 的名字。 来自易访问性的标签

  • 价值。 当前值的控制,例如,一个文本字段中的文本

  • 元素。 任何子元素包含在当前元素,例如,细胞在表视图中

  • 的父母。 包含当前元素的元素

理解元素层次结构

顶部是元素的层次结构 UIATarget 类,它代表了高层用户界面元素的被测系统(SUT),即设备(或模拟器)以及iOS应用程序上运行的设备。 出于测试的目的,你的应用程序是frontmost应用程序(或目标应用程序),确认如下:

UIATarget.localTarget().frontMostApp();

达到应用程序窗口,应用程序的主窗口中,您将指定

UIATarget.localTarget().frontMostApp().mainWindow();

在启动时,菜谱应用程序窗口所示 图成分 

在窗口中,菜谱列表提出了个人的观点,在这种情况下,表视图,请参阅 图剩下的 

图剩下的 配方表视图

这是第一个表视图应用一系列的表视图,因此您指定它使用零指数([0]),如下:

UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0];

在表视图,每个配方由不同的单个细胞。 以类似的方式您可以指定单个细胞。 例如,使用零指数([0]),您可以指定第一个单元格如下:

UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0].cells()[0];

每一个个体细胞元素被设计成包含配方记录作为一个自定义子元素。 在第一个细胞是巧克力蛋糕的记录,您可以访问的名字与这行代码:

UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0].cells()[0].elements()["Chocolate Cake"];

../Art/RecipesScreen01_tableView-cell.jpg

显示元素层次结构

您可以使用 logElementTree 任何元素方法列出它的所有子元素。 下面的代码演示了清单主要的元素(配方)食谱的屏幕(或模式)应用程序。

// List element hierarchy for the Recipes screen
UIALogger.logStart("Logging element tree …");
UIATarget.localTarget().logElementTree();
UIALogger.logPass();

命令的输出捕获日志中显示的自动化仪器,如 图11-5 

图11-5 输出从logElementTree方法

注意每个元素的数量开始行项目,表明元素的层次的。 这些水平可能认为从概念上说,是 图11:6 

图11:6 元素层次结构(食谱屏幕)

虽然屏幕不是技术上的iOS编程构造和不显式地出现在层次结构,它是一个有用的概念理解的层次结构。 开发单位转换选项卡的标签栏显示单位转换屏幕(或模式),所示图到第二天早上7点 

图到第二天早上7点 菜谱应用(单位转换屏幕)

下面的代码水龙头中的单位转换选项卡标签栏显示屏幕,然后记录相关元素相关联的层次结构:

// List element hierarchy for the Unit Conversion screen
var target = UIATarget.localTarget();
var appWindow = target.frontMostApp().mainWindow();
var element = target;
appWindow.tabBar().buttons()["Unit Conversion"].tap();
UIALogger.logStart("Logging element tree …");
element.logElementTree();
UIALogger.logPass();

由此产生的日志显示中所示的层次结构 图11 - 8 。 与前面的示例一样, logElementTree 叫做为目标,但当前的结果在这种情况下,单位转换屏幕。

图11 - 8 元素层次结构(单位转换屏幕)

简化元素层次结构导航

前面的代码示例介绍了使用的变量来表示部分元素的层次结构。 这种技术允许短,简单的命令在您的脚本。

以这种方式使用变量还允许一些抽象,产生代码使用和重用的灵活性。 下面的示例使用一个变量( destinationScreen )控制改变之间的两个主要屏幕(食谱和单位转换)的配方应用:

// Switch screen (mode) based on value of variable
var target = UIATarget.localTarget();
var app = target.frontMostApp();
var tabBar = app.mainWindow().tabBar();
var destinationScreen = "Recipes";
if (tabBar.selectedButton().name() != destinationScreen) {
    tabBar.buttons()[destinationScreen].tap();
}

只有细微的不同,这段代码代码工作,例如,对于一个标签栏与更多的标签或标签的名字。

执行用户界面的手势

一旦你了解如何访问所需的元素,这是相对简单和直接操纵该元素。

UI自动化API提供了执行大多数UIKit用户操作的方法,包括多点触控手势。 综合这些方法的详细信息,请参阅 UI自动化JavaScript参考 

攻丝。 也许最平易近人的姿态是一个简单的点击。 实现一个手指单点击一个已知的UI元素是非常简单的。 例如,利用正确的按钮,标有一个加号(+),导航栏的菜谱应用程序,显示一个新的屏幕用于添加一个新的配方。

../Art/RecipesAddButton.jpg

这个命令所需的全部操作按钮点击:

UIATarget.localTarget().frontMostApp().navigationBar().buttons()["Add”].tap();

注意,它使用这个名字 添加 确定按钮,可访问性标签已设置适当的假设,如上所述。

当然,更复杂的利用手势需要彻底测试任何复杂的应用程序,您可以指定任何标准丝锥的手势。 例如,利用一次在屏幕上的任意位置,你只需要提供屏幕坐标:

UIATarget.localTarget().tap({x:100, y:200});

这个命令水龙头在指定的x和y坐标,无论什么在这个位置在屏幕上。

更复杂的水龙头也可以。 手势是相同的位置,您可以使用这段代码:

UIATarget.localTarget().doubleTap({x:100, y:200});

知道并执行一个测试缩放,例如,你可以使用这段代码:

UIATarget.localTarget().twoFingerTap({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);

拖动和闪烁。 如果你需要滚动一个表或在屏幕上移动一个元素,您可以使用 dragFromToForDuration 方法。 你提供的起始位置和结束位置坐标,以及持续时间,以秒为单位。 下面的示例指定一个拖动手势从位置160年,200年到160年的位置,400年,在一段时间内1秒:

UIATarget.localTarget().dragFromToForDuration({x:160, y:200}, {x:160, y:400}, 1);

电影的姿态是相似的,但是它被认为是快速行动,所以它不需要一个时间参数。

UIATarget.localTarget().flickFromTo({x:160, y:200}, {x:160, y:400});

输入文本。 您的脚本可能需要测试你的应用程序在处理文本输入正确。 这样做,它可以将文本输入到一个文本字段,只需指定目标文本字段和设置它的值 setvalue方法。 下面的示例使用一个局部变量提供一长串作为第一个文本字段的测试用例(指数[0])在当前屏幕:

var recipeName = "Unusually Long Name for a Recipe";
UIATarget.localTarget().frontMostApp().mainWindow().textFields()[0].setValue(recipeName);

在你的应用程序选项卡导航。 测试之间的导航屏幕在你的应用程序,你很可能需要开发一个标签在一个标签栏。 开发一个标签就像开发一个按钮,您访问适当的标签栏,指定所需的按钮,和水龙头,按钮,如以下示例所示:

var tabBar = UIATarget.localTarget().frontMostApp().mainWindow().tabBar();
var selectedTabName = tabBar.selectedButton().name();
if (selectedTabName != "Unit Conversion")  {
    tabBar.buttons()["Unit Conversion"].tap();
    }

首先,声明一个局部变量来表示标签栏。 使用该变量,脚本访问标签栏来确定所选选项卡并得到标签的名字。 最后,如果选择选项卡的名称匹配所需的标签的名称(在本例中“单位转换”),该脚本水龙头,选项卡。

滚动一个元素。 滚动的很大一部分是与许多应用程序用户的交互。 UI自动化提供了各种方法来滚动。 滚动到下一个元素的基本方法允许左,右,或下降。 更复杂的方法支持更大的灵活性和特异性滚动操作。 其中一个方法是 scrollToElementWithPredicate ,它允许您滚动到您指定的元素,满足某些标准。 这个例子通过元素访问适当的表视图层次结构和卷轴的配方表视图的名称始于“海龟派”。

UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0].scrollToElementWithPredicate(“name beginswith ‘Turtle Pie’”);

使用 scrollToElementWithPredicate 方法允许滚动一个元素的确切名称可能不知道。

使用谓词的功能可以大大扩展脚本的功能和适用性。 使用谓词的更多信息,请参阅 谓词编程指南 

其他有用的方法灵活地滚动 scrollToElementWithName  scrollToElementWithValueForKey 。 看到 UIAScrollView类引用 获得更多信息。

添加时间的灵活性与超时时间

您的脚本可能需要等待一些行动完成。 配方的应用,例如,用户水龙头的食谱标签返回单位转换屏幕到屏幕的食谱。 然而,UI自动化可能检测Add按钮的存在,使测试脚本试图利用之前按钮实际上是吸引和挖掘的应用实际上是准备接受。 准确的测试必须确保食谱屏幕完全吸引,这个应用程序是准备接受用户交互与控件在屏幕之前。

提供一些灵活性,在这种情况下,给你更好的控制时间,UI自动化提供了超时时间,在这段期间里它会反复尝试失败之前执行指定的操作。 如果在超时期间操作完成,这行代码的回报,和脚本可以继续。 如果操作不完整的超时期间,就会抛出一个异常。 默认超时时间是5秒,但你的脚本可以在任何时候改变。

使这个功能尽可能容易使用,UI自动化使用堆栈模型。 你推一个定制的超时周期堆栈的顶部,与下面的代码,缩短两秒的超时时间。

UIATarget.localTarget().pushTimeout(2);

然后运行代码执行的操作和流行的超时值堆栈。

UIATarget.localTarget().popTimeout();

使用这种方法你最终得到一个健壮的脚本,一个合理的时间内等待事情发生。

注意: 尽管使用显式的延迟通常不鼓励,有时它可能是必要的。 下面的代码显示了如何指定一个延迟2秒:

UIATarget.localTarget().delay(2);

记录测试结果和数据

脚本报告日志信息自动化仪表,它收集和报告分析。

在编写测试时,您应该记录尽可能多的信息,如果只是为了帮助您诊断出现的任何故障。 在最低限度,您应该记录每个测试开始和结束的时候,确定测试执行和记录通过/失败状态。 这种最小日志几乎是自动在UI自动化。 你只是叫 logStart 您的测试的名称,运行您的测试,然后调用 logPass  logFail 作为适当,如以下示例所示:

var testName = "Module 001 Test";
UIALogger.logStart(testName);
//some test code
UIALogger.logPass(testName);

但它是一个很好的实践日志的情况当脚本与控制。 无论你是确认的部分应用程序执行正确或你还追踪bug,很难想象太多日志信息分析。 为此,您可以登录任何出现使用 logMessage ,你甚至可以补充的文本数据截图。

以下代码示例扩展日志的前面的示例包括一个自由格式的日志消息和截图:

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);

屏幕截图示例请求将被保存在仪器与指定的文件名( SS001-2_AddedIngredient 在这种情况下)。

注意: 目前不支持屏幕截图时针对iOS模拟器。 捕获屏幕快照的尝试,然而,表明一个日志消息指示一个失败的尝试。

验证试验结果

测试的关键是能够确认每个测试已经完成,并通过或失败。 这段代码示例运行测试 testName 确定一个有效的元素配方的名字从“称号”存在于配方表视图。 首先,一个局部变量用于指定细胞标准:

var cell = UIATarget.localTarget().frontMostApp().mainWindow().tableViews()[0].cells()
    .firstWithPredicate(“name beginswith ‘Tarte’”);

接下来,脚本使用 isValid 匹配这些标准的方法来测试是否有效元素存在于配方表视图。

if (cell.isValid()) {
    UIALogger.logPass(testName);
}
else {
    UIALogger.logFail(testName);
}

如果找到一个有效的细胞,日志的传递消息的代码 testName 测试;如果不是,这日志失败消息。

请注意,这个测试指定 firstWithPredicate  “名字beginsWith称号” 。 这些标准产量的引用单元的称号aux障碍物,这是默认的数据已经在食谱示例应用程序。如果,然而,用户添加一个秘方挞aux复盆子,这个例子可能会或可能不会给期望的结果。

处理警报

除了验证应用程序的提示正确执行,您的测试应该容纳警报出现出乎意料地从外部应用程序。例如,它是不寻常的一条短信,检查天气或者玩游戏。 更糟糕的是,一个电话销售自动拨号可以接你的电话的电话号码就像你启动脚本。

处理外部生成的警报

尽管它可能看起来有点矛盾,你的应用程序和测试应该料到会发生意想不到的警报当应用程序正在运行。 幸运的是,UI自动化包括违约警报处理程序,使外部警报脚本很容易应付。 您的脚本调用处理程序函数提供了一个警告 onAlert ,这叫做警报发生时,在这段时间里,它可以采取适当的行动,然后然后简单地返回解雇的默认处理程序。

下面的代码示例说明了一个非常简单的警报的例子:

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;
}

所有这些处理程序是日志消息,这种类型的警报发生然后返回  。 返回  指导UI自动化违约警报处理程序把警报。 在警报的情况下收到短信,例如,UI自动化点击关闭按钮。

注意: 默认处理程序停止解雇通知到达上限后顺序的警报。 在可能的情况下,您的测试达到这一限制时,你应该调查可能出现的问题与您的测试环境和程序。

处理内部生成的警报

作为应用程序的一部分,您将警报,需要处理。 在这些情况下,警报处理程序需要执行适当的响应并返回 真正的 默认处理程序,表明警报已经被处理。

以下代码示例小幅扩大基本警报处理程序。 日志记录警报类型后,测试警报是否特定的预期。 如果是这样,它可以继续按钮,这是已知的存在,并返回 真正的 跳过默认解雇行动。

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;
}

这个基本警报处理程序可以广义响应任何警报,同时允许脚本继续运行。

检测并指定设备定位

行为端正的iOS应用预计将优雅地处理设备定位的变化,所以你的脚本应该为这种变化预测和测试。

UI自动化提供了 setDeviceOrientation 模拟设备的改变方向。 该方法使用常量中列出 表十一 

注意: 至于设备定位处理,值得重复的功能是完全模拟软件。 硬件特性,比如原始加速度计数据都不可用于此UI自动化功能,不受其影响的。

表十一 设备定位常量

方向不变

描述

UIA_DEVICE_ORIENTATION_UNKNOWN

设备的方向不能确定。

UIA_DEVICE_ORIENTATION_PORTRAIT

设备在肖像模式下,设备直立和底部的home键。

UIA_DEVICE_ORIENTATION_PORTRAIT_UPSIDEDOWN

设备是在肖像模式但发生了天翻地覆的变化,与设备直立和顶部的home键。

UIA_DEVICE_ORIENTATION_LANDSCAPELEFT

横向模式的设备,设备直立和按钮在右边。

UIA_DEVICE_ORIENTATION_LANDSCAPERIGHT

横向模式的设备,设备直立和按钮在左边。

UIA_DEVICE_ORIENTATION_FACEUP

设备与屏幕平行于地面面临向上。

UIA_DEVICE_ORIENTATION_FACEDOWN

设备与屏幕平行于地面面临下行。

与设备方向界面取向,代表所需的旋转保持应用程序的面向接口的正确旋转装置。 注意,在横向模式,设备方向和界面方向是相反的,因为旋转设备需要旋转相反的方向的内容。

UI自动化提供了 interfaceOrientation 方法来获取当前界面取向。 该方法使用常量中列出 成分表 

成分表 面向接口的常量

方向不变

描述

UIA_INTERFACE_ORIENTATION_PORTRAIT

接口是在肖像模式下,接近底部按钮。

UIA_INTERFACE_ORIENTATION_PORTRAIT_UPSIDEDOWN

接口是在肖像模式但发生了天翻地覆的变化,与接近顶部按钮。

UIA_INTERFACE_ORIENTATION_LANDSCAPELEFT

横向模式的界面,左边靠近主按钮。

UIA_INTERFACE_ORIENTATION_LANDSCAPERIGHT

接口是在景观模式,最靠近右边按钮。

下面的示例更改设备方向(在这种情况下,景观左),然后改变它(肖像):

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());

当然,一旦你旋转,需要旋转回来。

当执行一个测试,包括改变设备的方向,这是一个很好的实践设置测试的开始旋转,然后把它回到原来的旋转结束时您的测试。 这种做法可以确保您的脚本总是回到一个已知状态。

您可能已经注意到的取向日志的例子。 这样的日志提供了额外的保证你的测试和testers-don不迷失方向。

测试任务

当用户退出你的应用程序利用家庭按钮或导致其他一些应用前景,应用程序暂停。 模拟这个发生,UI自动化提供了 deactivateAppForDuration 方法。 你调用这个方法,指定持续时间,以秒为单位,为你的应用程序暂停,如下例所示:

UIATarget.localTarget().deactivateAppForDuration(10);

这一行代码使程序释放了10秒,就好像一个用户退出应用程序并返回10秒后。

ios 性能优化之自动化UI测试