转载:http://debugtalk.com/post/build-app-automated-test-platform-from-0-to-1-Appium-interrogate-iOS-UI/node
前两天微信忽然发来一条系统消息,提示DebugTalk
能够开通原创标识了(同时也有了评论功能),虽然一直在期待,但没想到来得这么快,着实是个不小的惊喜。android
另外,最近在公众号后台也收到好几个朋友的信息,有的是询问某某部分何时能发布,有的是但愿能加快更新速度。说实话,收到这样的信息虽然会有压力,但真的挺开心的,由于这说明DebugTalk
至少能给一部分人带去价值,这说明这件事自己仍是值得坚持去作的。ios
不过,在更新频率这件事儿上,的确是要跟你们说抱歉了。由于DebugTalk
发布的内容全都是原创,主题基本上都是来源于我平常测试工做的经验积累,或者我近期学习一些测试技术的收获总结,这也意味着,我写的东西不少时候并非本身彻底熟悉的(彻底掌握的东西也没有足够的动力专门花时间去写)。ruby
就拿最近连载的《从0到1搭建移动App功能自动化测试平台》系列来讲,因为我也是边探索边总结,所以中途不免会遇到一些意想不到的坑,形成额外的耗时,并且为了保证文章能尽可能通俗易通,我也须要对涉及到的内容充分进行理解,而且通过大量实践进行验证,而后才能站在半个初学者、半个过来人的角度,从新整理思路,最后以尽量流畅的思路将主题内容讲解清楚。微信
基于这些缘由,DebugTalk
要作到每日更新是很难了,可是保证每周发布1~2篇仍是能够的,但愿你们能理解。app
在上一篇文章中,咱们成功地经过Appium Inspector调用模拟器并运行iOS应用,iOS的自动化测试环境也已所有准备就绪了。工具
那么接下来,咱们就能够开始实现自动化测试了么?post
貌似还不行。在开始以前,咱们先想下什么是APP功能自动化测试。学习
APP的功能自动化测试,简单地来讲,就是让功能测试用例自动地在APP上执行。具体到每个测试用例,就是能模拟用户行为对UI控件进行操做,自动化地实现一个功能点或者一个流程的操做。再细分到每一步,就是对UI控件进行操做。测试
所以,在正式开始编写自动化测试用例以前,咱们还须要熟悉如何与APP的UI控件进行交互操做。
在iOS系统中,UI控件有多种类型,常见的有按钮(UIAButton)、文本(UIAStaticText)、输入框(UIATextField)等等。但无论是对什么类型的UI控件进行操做,基本均可以分解为三步,首先是获取目标控件的属性信息,而后是对目标控件进行定位,最后是对定位到的控件执行动做。
在Appium中,要获取iOS的UI控件元素信息,能够采用两种方式:一种是在前一篇文章中提到的Appium Inspector,另外一种是借助Ruby实现的appium_console
,在Terminal中经过命令进行查询。
运行Appium Server,并启动【Inspector】后,总体界面以下图所示。
现对照着这张图对Appium Inspector进行介绍。
在右边部分,是启动的模拟器,里面运行着咱们的待测APP。咱们能够像在真机中同样,在模拟器中执行任意功能的操做,固然,模拟器跟真机毕竟仍是有区别的,跟传感器相关的功能,例如摄像头、重力感应等,是无法实现的。
在左边部分,就是Appium Inspector
。Inspector主要由以下四个部分组成:
在实践操做中,Inspector最大的用途就是在能够可视化地查看UI元素信息,而且能够将操做转换为脚本代码,这对初学者尤其有用。
例如,在预览区点击选中按钮“BUY NOW”,而后在UI信息展现区的Details窗口就能够看到该按钮的全部属性信息。在交互操做区点击【Tap】按钮后,就会模拟用户点击“BUY NOW”按钮,而且在脚本区域生成当次按钮点击的脚本(选择Ruby语言):
1
|
find_element(
:name, "BUY NOW >").click
|
如上就是使用Appium Inspector
的通常性流程。
有了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
|
经过返回信息,咱们就能够看到全部控件的type、name、label、value属性值。若是在某个控件下没有显示label或value,这是由于这个值为空,咱们能够不予理会。
因为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控件的属性信息后,就能够对控件进行定位了。
首先介绍下最通用的定位方式,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类型的控件(UIAStaticText),采用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
|
除此以外,基于Ruby实现的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
|
在本文中,咱们学习了对iOS UI控件进行交互操做的通常性方法,为编写自动化测试脚本打好了基础。
在下一篇文章中,咱们就要正式开始针对iOS应用编写自动化测试脚本了。