从0到1搭建移动App功能自动化测试平台(2):操作iOS应用的控件
2017-08-22 10:02
621 查看
转载:http://debugtalk.com/post/build-app-automated-test-platform-from-0-to-1-Appium-interrogate-iOS-UI/
另外,最近在公众号后台也收到好几个朋友的信息,有的是询问某某部分什么时候能发布,有的是希望能加快更新速度。说实话,收到这样的信息虽然会有压力,但真的挺开心的,因为这说明
不过,在更新频率这件事儿上,的确是要跟大家说抱歉了。因为
就拿最近连载的《从0到1搭建移动App功能自动化测试平台》系列来说,由于我也是边探索边总结,因此中途难免会遇到一些意想不到的坑,造成额外的耗时,而且为了保证文章能尽量通俗易通,我也需要对涉及到的内容充分进行理解,并且经过大量实践进行验证,然后才能站在半个初学者、半个过来人的角度,重新整理思路,最后以尽可能流畅的思路将主题内容讲解清楚。
基于这些原因,
那么接下来,我们就可以开始实现自动化测试了么?
貌似还不行。在开始之前,我们先想下什么是APP功能自动化测试。
APP的功能自动化测试,简单地来说,就是让功能测试用例自动地在APP上执行。具体到每一个测试用例,就是能模拟用户行为对UI控件进行操作,自动化地实现一个功能点或者一个流程的操作。再细分到每一步,就是对UI控件进行操作。
因此,在正式开始编写自动化测试用例之前,我们还需要熟悉如何与APP的UI控件进行交互操作。
在iOS系统中,UI控件有多种类型,常见的有按钮(UIAButton)、文本(UIAStaticText)、输入框(UIATextField)等等。但不管是对什么类型的UI控件进行操作,基本都可以分解为三步,首先是获取目标控件的属性信息,然后是对目标控件进行定位,最后是对定位到的控件执行动作。
现对照着这张图对Appium Inspector进行介绍。
在右边部分,是启动的模拟器,里面运行着我们的待测APP。我们可以像在真机中一样,在模拟器中执行任意功能的操作,当然,模拟器跟真机毕竟还是有区别的,跟传感器相关的功能,例如摄像头、重力感应等,是没法实现的。
在左边部分,就是
预览界面区:显示画面与模拟器界面一致;不过,当我们在模拟器中切换界面后,Inspector的预览区中显示图像并不会自动同步,若要同步,需要点击【Refresh】按钮,然后Inspector会将模拟器当前UI信息dump后显示到预览区;在预览区中,可以点击选择任意UI控件。
UI信息展示区:展示当前界面预览区中所有UI元素的层级关系和UI元素的详细信息;在预览区中点击选择任意UI控件后,在“Details”信息框中展示选中控件的详细信息,包括name、label、value、xpath等属性值;通过层级关系,我们也能了解选中控件在当前界面树状结构中所处的具体位置。
交互操作区:模拟用户在设备上的操作,例如单击(tap)、滑动(swipe)、晃动(shake)、输入(input)等;操作动作是针对预览界面区选中的控件,因此在操作之前,务必需要先在预览区点击选择UI元素。
脚本生成区:将用户行为转换为脚本代码;点击【Record】按钮后,会弹出代码区域;在交互操作区进行操作后,就会实时生成对应的脚本代码;代码语言可通过下拉框进行选择,当前支持的语言类型有:C#、Ruby、Objective-C、Java、node.js、Python。
在实践操作中,Inspector最大的用途就是在可以可视化地查看UI元素信息,并且可以将操作转换为脚本代码,这对初学者尤为有用。
例如,在预览区点击选中按钮“BUY NOW”,然后在UI信息展示区的Details窗口就可以看到该按钮的所有属性信息。在交互操作区点击【Tap】按钮后,就会模拟用户点击“BUY NOW”按钮,并且在脚本区域生成当次按钮点击的脚本(选择Ruby语言):
如上就是使用
其实,
但是,这并不意味着
在上一节中也说到了,Inspector最大的用途就是在可以可视化地查看UI元素信息,并且可以将操作转换为脚本代码。但是当我们对
在这种情况下,如果能有一种方式可以供我们快速查看当前屏幕的控件属性信息,那该有多好。
庆幸的是,在阅读
在使用
配置文件的名称统一要求为
其中,
在使用
然后,在Terminal中,进入到
接下来,就可以通过执行命令查询当前设备屏幕中的控件信息。
使用频率最高的一个命令是
例如,当屏幕停留在前面截图中的页面时,执行
通过返回信息,我们就可以看到所有控件的type、name、label、value属性值。如果在某个控件下没有显示label或value,这是因为这个值为空,我们可以不予理会。
由于
指定控件类型时,可以通过string类型进行指定(如 page “Image”),也可通过symbol类型进行指定(如 page :cell)。指定的类型可只填写部分内容,并且不分区大小写。
如果需要查看当前屏幕的所有控件类型,可以执行
基本上,
首先介绍下最通用的定位方式,
另一个通用的定位方式是
不过在实践中发现,采用
经过反复摸索,我推荐根据目标控件的类型,选择对应的定位方式。总结起来,主要有以下三种方式。
针对Button类型的控件(UIAButton),采用
针对Text类型的控件(UIAStaticText),采用
针对控件类型进行定位时,采用
基本上,这三种方式就已经足够应付绝大多数测试场景了。当然,这三种方式只是我个人经过实践后选择的定位方式,除了这三种,
另外,除了对控件进行定位,有时候我们还想判断当前屏幕中是否存在某个控件(通常用于结果检测判断),这要怎么做呢?
一种方式是借助于
该种方式可行,但比较暴力,基本上不会采用这种方式。
另一种更好的方式是,查找当前屏幕中指定控件的个数,若个数不为零,则说明控件存在。具体操作上,将
除此之外,基于Ruby实现的
操作类型不多,最常用就是点击(click)和输入(type),这两个操作能覆盖80%以上的场景。
对于点击操作,才定位到的控件后面添加
例如,账号登录操作就包含输入和点击两种操作类型。
在下一篇文章中,我们就要正式开始针对iOS应用编写自动化测试脚本了。
写在前面
前两天微信突然发来一条系统消息,提示DebugTalk可以开通原创标识了(同时也有了评论功能),虽然一直在期待,但没想到来得这么快,着实是个不小的惊喜。
另外,最近在公众号后台也收到好几个朋友的信息,有的是询问某某部分什么时候能发布,有的是希望能加快更新速度。说实话,收到这样的信息虽然会有压力,但真的挺开心的,因为这说明
DebugTalk至少能给一部分人带去价值,这说明这件事本身还是值得坚持去做的。
不过,在更新频率这件事儿上,的确是要跟大家说抱歉了。因为
DebugTalk发布的内容全都是原创,主题基本上都是来源于我日常测试工作的经验积累,或者我近期学习一些测试技术的收获总结,这也意味着,我写的东西很多时候并不是自己完全熟悉的(完全掌握的东西也没有足够的动力专门花时间去写)。
就拿最近连载的《从0到1搭建移动App功能自动化测试平台》系列来说,由于我也是边探索边总结,因此中途难免会遇到一些意想不到的坑,造成额外的耗时,而且为了保证文章能尽量通俗易通,我也需要对涉及到的内容充分进行理解,并且经过大量实践进行验证,然后才能站在半个初学者、半个过来人的角度,重新整理思路,最后以尽可能流畅的思路将主题内容讲解清楚。
基于这些原因,
DebugTalk要做到每日更新是很难了,但是保证每周发布1~2篇还是可以的,希望大家能理解。
关于UI控件
在上一篇文章中,我们成功地通过Appium Inspector调用模拟器并运行iOS应用,iOS的自动化测试环境也已全部准备就绪了。那么接下来,我们就可以开始实现自动化测试了么?
貌似还不行。在开始之前,我们先想下什么是APP功能自动化测试。
APP的功能自动化测试,简单地来说,就是让功能测试用例自动地在APP上执行。具体到每一个测试用例,就是能模拟用户行为对UI控件进行操作,自动化地实现一个功能点或者一个流程的操作。再细分到每一步,就是对UI控件进行操作。
因此,在正式开始编写自动化测试用例之前,我们还需要熟悉如何与APP的UI控件进行交互操作。
在iOS系统中,UI控件有多种类型,常见的有按钮(UIAButton)、文本(UIAStaticText)、输入框(UIATextField)等等。但不管是对什么类型的UI控件进行操作,基本都可以分解为三步,首先是获取目标控件的属性信息,然后是对目标控件进行定位,最后是对定位到的控件执行动作。
获取UI控件信息
在Appium中,要获取iOS的UI控件元素信息,可以采用两种方式:一种是在前一篇文章中提到的Appium Inspector,另一种是借助Ruby实现的appium_console,在Terminal中通过命令进行查询。
Appium Inspector
运行Appium Server,并启动【Inspector】后,整体界面如下图所示。现对照着这张图对Appium Inspector进行介绍。
在右边部分,是启动的模拟器,里面运行着我们的待测APP。我们可以像在真机中一样,在模拟器中执行任意功能的操作,当然,模拟器跟真机毕竟还是有区别的,跟传感器相关的功能,例如摄像头、重力感应等,是没法实现的。
在左边部分,就是
Appium Inspector。Inspector主要由如下四个部分组成:
预览界面区:显示画面与模拟器界面一致;不过,当我们在模拟器中切换界面后,Inspector的预览区中显示图像并不会自动同步,若要同步,需要点击【Refresh】按钮,然后Inspector会将模拟器当前UI信息dump后显示到预览区;在预览区中,可以点击选择任意UI控件。
UI信息展示区:展示当前界面预览区中所有UI元素的层级关系和UI元素的详细信息;在预览区中点击选择任意UI控件后,在“Details”信息框中展示选中控件的详细信息,包括name、label、value、xpath等属性值;通过层级关系,我们也能了解选中控件在当前界面树状结构中所处的具体位置。
交互操作区:模拟用户在设备上的操作,例如单击(tap)、滑动(swipe)、晃动(shake)、输入(input)等;操作动作是针对预览界面区选中的控件,因此在操作之前,务必需要先在预览区点击选择UI元素。
脚本生成区:将用户行为转换为脚本代码;点击【Record】按钮后,会弹出代码区域;在交互操作区进行操作后,就会实时生成对应的脚本代码;代码语言可通过下拉框进行选择,当前支持的语言类型有:C#、Ruby、Objective-C、Java、node.js、Python。
在实践操作中,Inspector最大的用途就是在可以可视化地查看UI元素信息,并且可以将操作转换为脚本代码,这对初学者尤为有用。
例如,在预览区点击选中按钮“BUY NOW”,然后在UI信息展示区的Details窗口就可以看到该按钮的所有属性信息。在交互操作区点击【Tap】按钮后,就会模拟用户点击“BUY NOW”按钮,并且在脚本区域生成当次按钮点击的脚本(选择Ruby语言):
1 | find_element(:name, "BUY NOW >").click |
Appium Inspector的一般性流程。
Appium Ruby Console
有了Appium Inspector,为什么还需要
Appium Ruby Console呢?
其实,
Appium Ruby Console也并不是必须的。经过与多个熟悉
Appium的前辈交流,他们也从未用过
Appium Ruby Console,这说明
Appium Ruby Console并不是必须的,没有它也不会影响我们对
Appium的使用。
但是,这并不意味着
Appium Ruby Console是多余的。经过这些天对
Appium的摸索,我越发地喜欢上
Appium Ruby Console,并且使用的频率越来越高,现在已基本上很少使用
Appium Inspector了。这种感觉怎么说呢?
Inspector相比于
Ruby Conosle,就像是
GUI相比于
Linux Terminal,大家应该能体会了吧。
Appium Inspector的功能是很齐全,GUI操作也很方便,但是,最大的问题就是使用的时候非常慢,在预览界面区切换一个页面常常需要好几秒,甚至数十秒,这是很难让人接受的。
在上一节中也说到了,Inspector最大的用途就是在可以可视化地查看UI元素信息,并且可以将操作转换为脚本代码。但是当我们对
Appium的常用API熟悉以后,我们就不再需要由工具来生成脚本,因为自己直接写会更快,前提是我们能知道目标控件的属性信息(type、name、label、value)。
在这种情况下,如果能有一种方式可以供我们快速查看当前屏幕的控件属性信息,那该有多好。
庆幸的是,在阅读
Appium官方文档时,发现
Appium的确是支持命令行方式的,这就是
Appium Ruby Console。
Appium Ruby Console是采用Ruby语言开发的,在使用方式上面和Ruby的
irb很类似。
在使用
Appium Ruby Console时,虚拟机的配置信息并不会从GUI中读取,而是要通过配置文件进行指定。
配置文件的名称统一要求为
appium.txt,内容形式如下所示:
1 2 3 4 5 | [caps] platformName = "ios" platformVersion = '9.3', app = "/path/to/UICatalog.app.zip" deviceName = "iPhone Simulator" |
platformName指定虚拟机操作系统类型,“ios”或者”android”;
platformVersion指定操作系统的版本,例如iOS的’9.3’,或者Android的’5.1’;
app指定被测应用安装包的路径。这三个参数是必须的,与Inspector中的配置也能对应上。
在使用
Appium Ruby Console时,首先需要启动
Appium Server,通过
GUI或者
Terminal均可。
然后,在Terminal中,进入到
appium.txt文件所在的目录,执行
arc命令即可启动
Appium Ruby Console。
arc,即是appium ruby console首字母的组合。
1 2 3 4 | ➜ ls appium.txt ➜ arc [1] pry(main)> |
使用频率最高的一个命令是
page,通过这个命令可以查看到当前屏幕中所有控件的基本信息。
例如,当屏幕停留在前面截图中的页面时,执行
page命令可以得到如下内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | [1] pry(main)> page UIANavigationBar name: HomeView id: Home => Home 米 => m 去看看 => View UIAButton name, label: tabbar category gray UIAImage name: debugtalk_logo.png UIAButton name, label: tabbar cart gray UIATableView value: rows 1 to 4 of 15 UIAPageIndicator value: page 2 of 2 UIATableCell name: For the first time ever in a hand held camera, the Osmo brings professional, realtime cinema-quality stabilization. id: 米 => m UIAStaticText name, label, value: For the first time ever in a hand held camera, the Osmo brings professional, realtime cinema-quality stabilization. id: 米 => m UIAStaticText name, label, value: OSMO UIAButton name, label: SHOP NOW > UIATableCell name: Ronin UIAStaticText name, label, value: Ronin UIAStaticText name, label, value: Phantom id: 米 => m ... (略) UIAButton name, label: Store value: 1 id: 门店 => Store ... (略) UIAButton name, label: My Account id: My Account => My Account nil |
由于
page返回的信息太多,可能不便于查看,因此在使用
page命令时,也可以指定控件的类型,相当于对当前屏幕的控件进行筛选,只返回指定类型的控件信息。
指定控件类型时,可以通过string类型进行指定(如 page “Image”),也可通过symbol类型进行指定(如 page :cell)。指定的类型可只填写部分内容,并且不分区大小写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [2] pry(main)> page "Image" UIAImage name: debugtalk_logo.png nil [3] pry(main)> page :cell UIATableCell name: DebugTalk’s smartest flying camera ever. id: 米 => m UIATableCell name: Ronin UIATableCell name: Phantom id: 米 => m nil |
page_class命令进行查看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [4] pry(main)> page_class 14x UIAButton 8x UIAStaticText 4x UIAElement 4x UIATableCell 2x UIAImage 2x UIAWindow 1x UIAPageIndicator 1x UIATableView 1x UIAStatusBar 1x UIANavigationBar 1x UIATabBar 1x UIAApplication nil |
page返回的控件信息已经足够满足绝大多数场景需求,但有时候情况比较特殊,需要
enabled、
xpath、
visible、坐标等属性信息,这时就可以通过执行
source命令。执行
source命令后,就可以返回当前屏幕中所有控件的所有信息,以xml格式进行展现。
定位UI控件
获取到UI控件的属性信息后,就可以对控件进行定位了。首先介绍下最通用的定位方式,
find。通过
find命令,可以实现在控件的诸多属性值(
name、
label、
value、
hint)中查找目标值。查询时不区分大小写,如果匹配结果有多个,则只返回第一个结果。
1 2 3 4 | [5] pry(main)> find('osmo') #<Selenium::WebDriver::Element:0x..febd52a30dcdfea32 id="2"> [6] pry(main)> find('osmo').label "Osmo" |
find_element,它也可以实现对所有控件进行查找,但是相对于
find,可以对属性类型进行指定。
1 2 3 4 | [7] pry(main)> find_element(:class_name, 'UIATextField') #<Selenium::WebDriver::Element:0x31d87e3848df8804 id="3"> [8] pry(main)> find_element(:class_name, 'UIATextField').value "Email Address" |
find、
find_element这类通用的定位方式并不好用,因为定位结果经常不是我们期望的。
经过反复摸索,我推荐根据目标控件的类型,选择对应的定位方式。总结起来,主要有以下三种方式。
针对Button类型的控件(UIAButton),采用
button_exact进行定位:
1 2 | [9] pry(main)> button_exact('Login') #<Selenium::WebDriver::Element:0x..feaebd8302b6d77cc id="4"> |
text_exact进行定位:
1 2 | [10] pry(main)> text_exact('Phantom') #<Selenium::WebDriver::Element:0x1347e89100fdcee2 id="5"> |
tag;如下方式等价于
find_element(:class_name, 'UIASecureTextField')。
1 2 | [11] pry(main)> tag('UIASecureTextField') #<Selenium::WebDriver::Element:0x..fc6f5efd05a82cdca id="6"> |
Appium还支持很多种其它定位方式,大家可自行查看
Appium官方文档进行选择。
另外,除了对控件进行定位,有时候我们还想判断当前屏幕中是否存在某个控件(通常用于结果检测判断),这要怎么做呢?
一种方式是借助于
Appium的控件查找机制,即找不到控件时会抛出异常(
Selenium::WebDriver::Error::NoSuchElementError);反过来,当查找某个控件抛出异常时,则说明当前屏幕中不存在该控件。
1 2 3 | [12] pry(main)> button_exact('Login_invalid') Selenium::WebDriver::Error::NoSuchElementError: An element could not be located on the page using the given search parameters. from /Library/Ruby/Gems/2.0.0/gems/appium_lib-8.0.2/lib/appium_lib/common/helper.rb:218:in `_no_such_element' |
另一种更好的方式是,查找当前屏幕中指定控件的个数,若个数不为零,则说明控件存在。具体操作上,将
button_exact替换为
buttons_exact,将
text_exact替换为
texts_exact。
1 2 3 4 | [12] pry(main)> buttons_exact('Login').count 1 [13] pry(main)> buttons_exact('Login_invalid').count 0 |
appium_lib还支持
exists方法,可直接返回Boolean值。
1 2 3 4 | [14] pry(main)> exists { button_exact('Login') } true [15] pry(main)> exists { button_exact('Login_invalid') } false |
对控件执行操作
定位到具体的控件后,操作就比较容易了。操作类型不多,最常用就是点击(click)和输入(type),这两个操作能覆盖80%以上的场景。
对于点击操作,才定位到的控件后面添加
.click方法;对于输入操作,在定位到的输入框控件后面添加
.type方法,并传入输入值。
例如,账号登录操作就包含输入和点击两种操作类型。
1 2 3 4 5 6 | [16] pry(main)> find_element(:class_name, 'UIATextField').type 'leo.lee@debugtalk.com' "" [17] pry(main)> find_element(:class_name, 'UIASecureTextField').type '123456' "" [18] pry(main)> button_exact('Login').click nil |
To be continued …
在本文中,我们学习了对iOS UI控件进行交互操作的一般性方法,为编写自动化测试脚本打好了基础。在下一篇文章中,我们就要正式开始针对iOS应用编写自动化测试脚本了。
相关文章推荐
- Appium 从 0 到 1 搭建移动 App 功能自动化测试平台 (1):模拟器中运行 iOS 应用
- 从0到1搭建移动App功能自动化测试平台(0):背景介绍和平台规划
- 当Xcode大于等于8时,如何使用Appium Desktop 获取iOS APP控件的id进行自动化测试
- iOS应用程序安全(1)-搭建移动渗透测试平台
- android ios 自动化测试平台搭建
- APP自动化(1)——搭建Appium自动化测试平台环境(基于python&android)
- iOS应用程序安全(1)-搭建移动渗透测试平台
- Testin云测试,免费的移动App应用真机自动化云测试
- iOS平台iPhone iPad和iPod Touch开源应用游戏App源代码下载
- 一种移动APP统计平台的架构方案(适用于千万级用户的应用)
- Twitter的照片滤镜功能可能已经上线,内部员工正在对IOS和Android平台应用程序上进行测试
- iOS开发之移动应用海外推广平台资源总结
- 使用UI Automation实现自动化测试--7.2 (模拟键盘复杂操作在自动化测试中的应用)
- 流行移动应用自动化测试开源框架/工具之优劣势比较
- 实施移动应用自动化测试
- SAP企业移动平台开发探索系列4 – 使用SUP开发iOS离线应用
- Mac os 下java中调用外部App(iOS自动化测试实践过程记录)
- iOS_iPhone App自动化测试
- iOS开发之移动应用海外推广平台资源总结(持续更新...)
- (jenkins)hudson平台搭建android项目持续化集成以及基于NativeDriver的自动化测试环境