核心实验:利用Appium测试原生应用
原生应用程序是App最为主流的一种程序类型,也是Appium发挥作用最重要的场景。本实验就基于Appium来对“一笔记账”应用进行测试。
原生应用程序是App最为主流的一种程序类型,也是Appium发挥作用最重要的场景。本实验就基于Appium来对“一笔记账”应用进行测试。
(1)深入理解对象识别机制及Appium的使用。
1.界面元素识别
在利用Selenium针对标准Web应用进行测试时,我们可以使用浏览器自带的开发人员工具来识别元素的各种属性,也可以利用Selenium IDE插件来获取到HTML元素的属性,并用于测试脚本中。这个过程同样适应于Appium的移动设备,主要利用Android SDK自带的UI Automator Viewer工具进行元素的属性识别。通常情况下,在移动端的应用有三种类型:
(1)原生应用(Native App):利用原生的移动操作系统提供的接口开发的应用程序,功能最强大,但是在兼容性方面相对容易出问题。尤其是在Android端,由于过多的定制Android系统,设备生产商,不同的分辨率等。所以原生应用在研发过程中也是成本最高的一种,其测试工作也非常复杂繁琐,当然,也正因为如此,针对原生应用的自动化测试产生的价值也最明显,尤其是在名目繁多的设备上进行兼容性测试。
(2)Web应用(Web App):WebApp严格意义上来说就是一个浏览器应用,对移动端的分辨率,功能方面进行了适配。Web应用的优势在于轻量级,满足W3C定义的规范,兼容性高,一次开发,到处运行,不需要经常提醒用户更新,对兼容性测试的要求不高,能够节省大量人力成本和时间成本。但是由于浏览器对直接操作手机端的权限的控制,导致WebApp除了利用HTML+JavaScript能做的事情外,很难直接与操作系统对接,完成一些底层的操作,比如直接操作移动设备硬件如相机,GPS等。
(3)混合应用(Hybrid App):正因为上述两种应用程序的优势和不足都非常明显,所以目前更多的时候大家会选择使用混合应用,即利用原生应用做窗口和框架,利用Web应用完成基本功能实现。能实现混合应用的核心在于原生应用内置的WebView控件(即WebKit渲染引擎,用于对Web页面进行渲染,对JavaScript脚本进行解析等),无论是Android还是iOS,其WebView控件均使用的是WebKit引擎。这种混合应用可以很好地结合原生应用和Web应用的优点,从而提升用户体验,降低研发成本。
事实上,无论针对哪种应用程序类型,Appium都可以完成测试,并且使用同样的一套标准测试接口,对于对象的识别和操作也都是一致的。
目前将对象识别的常用API列举如下:
(1)driver.find_element_by_id(“resouce-id”):根据元素的resouce-id属性进行识别。
(2)driver.find_element_by_name(“text”):根据元素的text属性进行识别。目前该查找方式在最新版本的Appium中已经被取消,但是我们可以通过修改Appium源代码的方式继续启用,因为这个API也是使用频率非常高的。只需要修改文件C:\Users\Denny\AppData\Roaming\npm
\node_modules\appium\node_modules\appium-android-driver\build\lib\driver.js,找到“this.locatorStrategies = ['xpath', 'id', 'class name', 'accessibility id', '-android uiautomator'];”在其列表最后添加’name’字段变成“this.locatorStrategies = ['xpath', 'id', 'class name', 'accessibility id', '-android uiautomator', 'name'];”并重启Appium服务器即可生效。
(3)driver.find_element_by_xpath(“xpath”):利用XPath对元素进行识别。
(4)driver.find_element_by_link_text(“link”):根据Web页面的超链接文本对元素进行识别。
(5)driver.find_element_by_partial_link_text(“link”):根据超链接的部分文本进行识别。
(6)driver.find_element_by_class_name(“css”):根据元素的Class属性进行识别。
(7)driver.find_element_by_css_selector(“selector”):根据CSS选择器进行识别。
(8)driver.find_element_by_tag_name(“tag”):根据标签名称进行识别。
(9)driver.find_element_by_accessibility_id(“content-desc”):根据可访问到的文本即content-desc属性进行识别。
(10)driver..find_element_by_android_uiautomator(“uia-selector”):根据Android原生的UI Automator库的识别对象的方式进行识别。Appium本身就是对UIAutomator进行的二次封装,所以不太会用到。
(11)driver.find_element_by_ios_uiautomation(“ios-selector”):根据iOS原生框架的识别方式进行识别。
(12)driver.find_elements_by_xxxxx():针对上述识别方式的复数形式,即识别1个或多个元素,识别到的元素将返回在一个列表中,通常情况下,如果我们利用相对的识别属性无法精准地定位某一个特定元素时,可以使用复数进行识别。再通过遍历的方式来判断这些识别到的元素的某些属性来进一步找到测试过程中需要的那一个。
2.开发“一笔记账”测试脚本
(1)设计测试用例,明确测试点和操作步骤。
(2)利用UI Automator Viewer识别对应的元素属性。
(3)开发Appium脚本,并完成对用例的断言。
(4)调试脚本,确保流畅运行,并达到自动化测试目的。
由于本节内容重点在于对象识别和测试脚本开发,所以我们先来完成一个相对简单的测试流程,将重心放在Appium测试脚本的开发中。主要针对“一笔记账”完成一个记账操作,主要操作界面为:
根据上图所示的操作步骤,现在使用UI Automator Viewer工具来完成对象属性的识别。首先来看看第一屏的“账目列表”和“记一笔”两个对象的属性情况。如图所示。
通过上图的属性识别后,我们首先需要来规划对象识别的操作方案,因为再好的自动化测试,如果没有办法准确地找到我们需要的元素,将变得毫无意义。
(1)账目列表有多行,每一行主要由两个内容构成:账目备注和所花费用。而账目备注的resource-id属性值为“com.mobivans.onestrokecharge:id/account_item_txt_remark”,所花费用的resource-id属性值为“com.mobivans.onestrokecharge:id/account_item_txt_money”。这本身没有问题,使用ID属性定位元素是最高效的做法,但是现在的问题是账目列表中的每一行元素,都是这两个相同的resource-id属性,那么,我们使用driver.find_element_by_id方法就无法精确定位到我们需要找的那一行。
这种情况下该怎么处理呢?
既然元素有resource-id属性,且有多个元素具备相同的resource-id属性,那么我们可以使用driver.find_elements_by_id复数形式的方法来获取所有相同ID属性的元素,然后遍历这些元素,再利用元素的get_attribute(“text”)方法或者.text属性获取到对应的text属性值,进而判断该值是否与我们输入的值一致,进而达到断言的目的。
(2)对于按钮“记一笔”来说,这是我们完成核心操作的第一步,必须准确定位到该元素上。通过UI Automater Viewer的识别我们可以看到,该添加按钮是一个图标带文本的按钮,即我们可以操作图标完成添加,也可以直接点击正文文本完成添加。但是这个图文按钮都没有resource-id属性。
这种情况下又该如何处理呢?
我们可以通过两种方案来解决。一是该图文按钮的上一层是一个LinearLayout的布局对象,并且具备resource-id属性“com.mobivans.onestrokecharge:id/main_write1”,该元素是图文按钮的父元素,我们通过先寻找到其父元素,再寻找子元素的方式,利用XPath定位即可找到。另外一种方式是直接利用文本按钮的text属性,使用driver.find_element_by_name方法查找到该元素,完成操作。
当完成了第一屏的元素分析后,我们继续来完成第二屏的元素分析。在第二屏中的元素,除了选择支出类型的地方使用的是图文按钮外,其它元素都有对应的resource-id属性,现列举如下:
(1)按钮“支出”,resource-id为“com.mobivans.onestrokecharge:id/add_txt_Pay”,text为“支出”。
(2)按钮“书籍“,resource-id为“com.mobivans.onestrokecharge:id/item_cate_image”,但是所有类别的resource-id均为相同属性,所以不可用,退而求其次,使用正文文本按钮,利用driver.find_element_by_name方法查找其text属性即可。
(3)文本框“备注”,resource-id为“com.mobivans.onestrokecharge:id/add_et_remark”,唯一识别。
(4)下方数据按钮,每一个按钮对应一个resource-id属性,且唯一识别。
(5)按钮“完成”,resource-id为“com.mobivans.onestrokecharge:id/keyb_btn_finish”,唯一识别。
对上述界面元素进行分析和找到定位方案后,接下来完成以下测试脚本的开发。
from appium import webdriver from time import sleep from selenium.webdriver.common.action_chains import ActionChains import os
app_path = os.path.abspath('.') + '\\yibijizhang.apk' # 定义apk文件
desired_caps = {} # 定义webdriver的兼容性设置字典对象 desired_caps['platformName'] = 'Android' # 指定测试Android平台 desired_caps['platformVersion'] = '4.4.2' # 指定移动端的版本号 desired_caps['deviceName'] = 'Appium' # 指定设备名称 desired_caps['unicodeKeyboard'] = 'True' desired_caps['app'] = app_path # 如已安装可不指定 # 通过aapt命令输出的package取得启动的主包名 desired_caps['appPackage'] = 'com.mobivans.onestrokecharge' # 通过aapt输出的launchable-activity取得主Activity名 desired_caps['appActivity'] = 'com.stub.stub01.Stub01' desired_caps['udid'] = '127.0.0.1:62001' # 指定设备编号
# 实例化webdriver,并指定appium服务器访问地址,一定要加上/wd/hub driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps) sleep(3)
# driver.find_element_by_name("记一笔").click() # 点击添加按钮
# 添加按钮也可以使用XPath定位找到记一笔的图形按钮, XPath中的resource-id不要简写 driver.find_element_by_xpath("//android.widget.LinearLayout[@resource-id='com.mobivans.onestrokecharge:id/main_write1']/android.widget.ImageView[1]").click()
driver.find_element_by_id("add_txt_Pay").click() # 点击支出 driver.find_element_by_name("书籍").click() # 选择书籍 # 添加备注,使用send_keys发送文本内容 driver.find_element_by_id("add_et_remark").send_keys(u'购买自动化测试教材') driver.find_element_by_id("keyb_btn_5").click() driver.find_element_by_id("keyb_btn_6").click() driver.find_element_by_id("keyb_btn_finish").click() sleep(2)
# 由于账目列表会存在多个,所以此处断言需要使用复数形式来先获取一个元素列表 list_remark = driver.find_elements_by_id("account_item_txt_remark") list_money = driver.find_elements_by_id("account_item_txt_money") if (list_remark[0].text == "购买自动化测试教材" and list_money[0].text == "-56"): print("测试成功.") else: print("测试失败.")
# 删除该支出项 TouchAction(driver).long_press(detail[0]).perform() driver.find_element_by_id("alert_tv_ok").click()
driver.quit() # 结束测试 |
上述代码完成了针对一个原生Android应用的测试开发。并且对常见的一些对象识别方式进行了应用。其实针对GUI界面的自动化测试,无论是针对PC端还是移动端,首先一定要想办法识别到对象,然后才能对此进行操作。
另外,Android SDK自带的UI Automator Viewer虽然已经足够使用,但是在识别对象时特别是需要利用XPath来识别对象时,我们需要自己完成XPath的编写,难免会容易出错。所以,在基于UI Automator Viewer的基础上,提供了另外一个功能更加强大的Lazy UI Automator Viewer,除了具备标准版的所有功能外,还帮助我们自动生成XPath和UiaSelector(即可以使用driver.find_element_by_android_uiautomator()方法进行对象操作),当某些元素没有text,没有resource-id,没有content-desc或者不能唯一识别时,那么Lazy UI Automator Viewer提供的XPath和UiaSelector将为我们提供效率更高的对象识别方案。
直接在网络上搜索Lazy UI Automator Viewer并完成下载,这是一个11MB左右大小的jar文件,命名为“uiautomatorviewer.jar”。下载完成后,将该文件替换掉Android SDK自带的同名文件(在SDK目录下的tools\lib目录下),然后重新启动uiautomatorviewer.bat即可。下图展示了利用Lazy UI Automator Viewer识别一笔记账应用中的“备注”文本框的XPath和Uia识别属性。如图所示。
Lazy UI Automator Viewer只是一个帮助我们快速书写XPath或UiaSelector表达式的辅助工具而已,并没有什么神奇的功能。无法识别的对象仍然无法识别。但是针对可以识别的对象,可以却可以帮助我们更快速地完成定位。所以,后续章节内容我们将更多的使用该工具。
(1)在应用商店下载各类应用,并利用Appium对其进行测试。