Appium在Android UI测试中的应用实践

简介:

Android 测试工具与 Appium 简介

Appium 是一个 C/S 架构的,支持 Android/iOS Native, Hybrid 和 Mobile Web Apps 的测试框架,与测试程序通过 Selenum Webdriver 协议通讯。Webdriver 的好处是通过 HTTP RPC 的方式调用 Server 上的过程,编写测试脚本不受语言的限制,无论是 Python, Java, NodeJS 均可以方便的编写测试。本文中将使用 Python 进行编程。

起因是因为市场部的同事抛来如下需求:批量添加一些微信好友。直接抓取请求进行重放的方法是不靠谱的,微信与服务端的通讯均加密,Pass。考虑使用 xposed 等框架 hook 相关函数进行操作。但是 xposed 需要越狱,且开发复杂,Pass。后来想到了使用 UI 测试工具进行模拟操作,开发较为简单。

Android UI 测试工具有很多种,如 Monkey, UIAutomator, Selendroid, Robotium 等。其中 UIAutomator, Monkey, Selendroid 均为非侵入式的 UI 测试,也就是不需要修改源代码,只要安装了目标程序就可以进行测试。Robotium 需要与源码一同编译测试。Appium 实际上就是一个测试工具的统一调度软件,将不同的非侵入式测试工具整合在一起,对外提供统一的 API。在 Android 2.3 以前的版本,Appium 会调用 Selendroid ,之后的版本会直接使用 UIAutomator,iOS 下使用 UIAutomation。Appium 还支持 FirefoxOS 的 UI 测试。

安装 Appium

官网给出了命令行下的安装方法。但实际上 Appium 有 GUI 版本,更适合在 Windows/MacOS 下使用。Windows 下需要安装 .NET Framework。


  
  
  1. > brew install node      # get node.js 
  2. > npm install -g appium  # get appium 
  3. > npm install wd         # get appium client 
  4. > appium &               # start appium 
  5. > node your-appium-test.js 

Appium 需要依赖 Android SDK 编译在手机端运行的两个插件,因此需要首先安装相应的 Android SDK 版本。这里直接使用了 Android Studio 中自带的 SDK Manager。在 SDK Manager 中选择和测试机相对应的 SDK Platform 和较新的 Build-tools,如果需要使用模拟器测试还要装对应的 ARM/x86 System Image,以及 Intel HAXM Installer,用于加速 x86 虚拟机。Appium 使用 adb 来与目标机器通讯,因此对于真机和模拟器操作几乎都是相同的,如何建立模拟器在此不再赘述。

安装完成后需要在 Appium GUI 中配置 Android SDK 目录,随后选择 Android,点击 Launch 就可以启动 Appium Server。

Appium Server 默认会监听 http://localhost:4723 ,用于 RPC 通讯。下面我们就可以打开熟悉的编程环境,编写 UI 测试用例了。这里使用 Python 进行编写,需要先安装 Appium 的 Python Client ,然后再 python 中使用 appium.webclient 就可以连接 Appium server了。


  
  
  1. pip install Appium-Python-Client 

使用 Appium 进行 UI 控制

根据注释修改相应属性后即可运行测试。手机需要打开 ADB 调试,执行完以下代码后,Appium 会在手机上安装 Appium Settings 和 Unlock 两个程序,随后微信会被启动。


  
  
  1. from appium import webdriver 
  2.  
  3. desired_caps = {} 
  4.  
  5. desired_caps['platformName'] = 'Android' #测试平台 
  6.  
  7. desired_caps['platformVersion'] = '5.1' #平台版本 
  8.  
  9. desired_caps['deviceName'] = 'm3_note' #设备名称,多设备时需区分 
  10.  
  11. desired_caps['appPackage'] = 'com.tencent.mm' #app package名 
  12.  
  13. desired_caps['appActivity'] = '.ui.LauncherUI' #app默认Activity 
  14.  
  15. dr = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) #启动Remote RPC 

Selenum Webdriver 使用了一种类似于 JS 中的 DOM 模型的方法来选择页面中的元素。dr 为当前正在活动的 activity 对象,可以使用 findElementByXXX 的方法来获取 Activity 中的元素。所有 Element 后带 s 的函数,均获得所有匹配的元素,不带 s 的函数获得第一个匹配的元素。

查询函数

1. findElement(s)ByName

在 Android 中基本没用。Android UI 没有 Name 这个属性。有说可以使用 text 值获取。但我并没有成功

2. findElement(s)ByClassName

通过类名来获取元素,用法如下:


  
  
  1. item_list = dr.find_elements_by_class_name("android.widget.LinearLayout"
  2. item_list[2].click() 

3. findElementById

通过 resource_id 来获取元素,每个 Activity 中都是唯一的,用法如下


  
  
  1. t = dr.find_element_by_id("com.tencent.mm:id/f7"
  2. t.send_keys(wechatId) 

4. findElement(s)ByAccessbiltiyId

在 Android 上 AccessbilityID 实际就是 contentDescription 。这个属性是为了方便视力受损人士使用手机所设置。开启 TTS 后系统会朗读相关控件的 contentDescription。

5. findElement(s)ByXPath

通过 XML Path 描述来寻找元素。我没有成功的获取到,可能是 XPath 写的有问题。


  
  
  1. s = dr.find_element_by_xpath("//android.widget.TextView[contains(@text,'搜索')]"
  2. s.click() 

6. findElementByAndroidUIAutomator

通过 UIAutomator 的选择器来获取元素。因为 Appium 在 Android 上实际是调用的 UIAutomator,所以可以通过 UIAutomator 的选择器来选择元素。


  
  
  1. el = dr.find_element_by_android_ui_automator("new UiSelector().text(\"搜索\")"
  2. el.click() 

操作函数

操作函数用于操作选定的元素,有很多,以下仅列举几个,更多的请查阅手册。

  • click
  • send_keys
  • clear

查询函数返回的元素对象可以像 JS 中的 dom 元素一样,继续使用查询函数来选定其子元素。用例如下。


  
  
  1. search = dr.find_element_by_id("com.tencent.mm:id/aqw").find_element_by_class_name("android.widget.RelativeLayout"
  2.  
  3. search.click() 

如何确定查询规则

了解了相关的函数后,下面就应对 UI 进行定位了。如果是自己团队开发的程序,推荐让开发同学在所有的空间上都添加 resource_id 进行绝对定位。如果碰到没有谈价 resource_id 的元素,那就要使用别的办法进行定位了。

1. UI Automator Viewer

UI Automator Viewer 是 Android 官方的 UI 定位工具,位于 sdk/tools 下。运行后会打开 viewer 界面。点击获取按钮即可获取当前正在运行的 Activity 的 UI 结构。

2. AppiumDriver getPageSource

AppiumDriver(Client) 可以很方便的获得当前正在运行的 Activity 的 UI 描述,随后可根据返回的 XML 文档来寻找元素。


  
  
  1. print dr.page_source 

确定元素位置后,即可根据前述的 Find 方法来查找/选择元素

编写完整的测试代码

正确的获取元素之后便可以获取元素相关的信息,随后使用各语言常用的测试框架编写测试即可,如 Java 的 JUnit,Nodejs 的 Mocha 等。

这里我使用 Appium 主要是为了模拟用户点击添加微信好友,所以完整的程序并没有使用到测试框架。相关的 UI 元素获取/操作方法供大家参考。


  
  
  1. # coding:utf-8 
  2. from appium import webdriver 
  3. from time import sleep 
  4.  
  5.  
  6. def addFriend(dr, id, dryRun=False): 
  7.     succ = False 
  8.     wechatId = str(id) 
  9.     dr.find_element_by_accessibility_id(r"更多功能按钮").click() 
  10.     item_list = dr.find_elements_by_class_name("android.widget.LinearLayout"
  11.     try: 
  12.         item_list[2].click() 
  13.     except
  14.         print "Error! in item list len" 
  15.         return succ 
  16.     el = dr.find_element_by_class_name("android.widget.ListView"
  17.     item_list = el.find_elements_by_class_name("android.widget.LinearLayout"
  18.     try: 
  19.         item_list[1].click() 
  20.     except
  21.         print "Error! in item list len" 
  22.         return succ 
  23.     t = dr.find_element_by_id("com.tencent.mm:id/f7"
  24.     t.send_keys(wechatId) 
  25.     search = dr.find_element_by_id("com.tencent.mm:id/aqw").find_element_by_class_name("android.widget.RelativeLayout"
  26.     search.click() 
  27.     try: 
  28.         freq = dr.find_element_by_id('com.tencent.mm:id/aqq'
  29.         assert freq.text == u"操作过于频繁,请稍后再试。" 
  30.         print "Frequency too high! Sleep 300s" 
  31.         sleep(60) 
  32.         return succ 
  33.     except
  34.         pass 
  35.  
  36.     try: 
  37.         dr.find_element_by_id('com.tencent.mm:id/a8x').click() 
  38.         addBtn = dr.find_element_by_id('com.tencent.mm:id/eu'
  39.         if not dryRun: 
  40.             addBtn.click() 
  41.             succ = True 
  42.         print "Success Send Requests:" + wechatId 
  43.     except
  44.         print "No Such User Or Already a Friend:" + wechatId 
  45.  
  46.     while True
  47.         try: 
  48.             dr.find_element_by_id('com.tencent.mm:id/fb').click() 
  49.         except
  50.             try: 
  51.                 dr.find_element_by_id('com.tencent.mm:id/f4').click() 
  52.             except
  53.                 break 
  54.     return True 
  55.  
  56. def resetActivity(dr, desired_caps): 
  57.     dr.start_activity(desired_caps['appPackage'], desired_caps['appActivity']) 
  58.  
  59. desired_caps = {} 
  60. desired_caps['platformName'] = 'Android' 
  61. desired_caps['platformVersion'] = '5.1' 
  62. desired_caps['deviceName'] = 'm3_note' 
  63. desired_caps['appPackage'] = 'com.tencent.mm' 
  64. desired_caps['appActivity'] = '.ui.LauncherUI' 
  65. print "Trying connect to phone..." 
  66. dr = {} 
  67. try: 
  68.     dr = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) 
  69. except Exception, e: 
  70.     print "Cannot Connect to phone :", e 
  71.     exit() 
  72. print "Successfully connect to phone." 
  73. print "Reading friend list..." 
  74. friendList = [] 
  75. fp = open("friends.txt"
  76. line = fp.readline().strip() 
  77. while line: 
  78.     friendList.append(line) 
  79.     line = fp.readline().strip() 
  80. print "Finish reading friends. Total: " + str(len(friendList)) 
  81. print "Wait for Wechat's splash screen...." 
  82. for i in range(0, 10): 
  83.     print 10 - i 
  84.     sleep(1) 
  85. succ_list = [] 
  86. fail_list = [] 
  87. for i in friendList: 
  88.     try: 
  89.         succ = addFriend(dr, i, dryRun=False
  90.         if succ: 
  91.             succ_list.append(i) 
  92.         else
  93.             fail_list.append(i) 
  94.     except
  95.         fail_list.append(i) 
  96.         resetActivity(dr, desired_caps) 
  97.  
  98. print "Succeed List:" 
  99. print "\n".join(succ_list) 
  100. print "Failed List:" 
  101. print "\n".join(fail_list) 
  102.  
  103. dr.close() 





本文作者:佚名
来源:51CTO
目录
相关文章
|
15天前
|
敏捷开发 测试技术 持续交付
探索自动化测试在敏捷开发中的应用与挑战
本文深入探讨了自动化测试在现代软件开发流程,特别是敏捷开发环境中的重要作用和面临的挑战。通过分析自动化测试的基本原理、实施策略以及在实际项目中的应用案例,揭示了其在提高软件质量和加速产品交付方面的巨大潜力。同时,文章也指出了自动化测试实施过程中可能遇到的技术难题、成本考量及团队协作问题,并提出了相应的解决策略,为软件开发团队提供了有价值的参考和指导。
|
19天前
|
编解码 测试技术 开发工具
测试 iPhone 应用在不同屏幕尺寸和分辨率下的响应式效果
【10月更文挑战第23天】测试 iPhone 应用在不同屏幕尺寸和分辨率下的响应式效果是确保应用质量和用户体验的重要环节。通过手动测试、自动化测试、视觉效果评估、性能测试、用户体验测试等多种方法的综合运用,能够全面地发现应用在响应式效果方面存在的问题,并及时进行解决和优化。同时,持续的测试和优化也是不断提升应用质量和用户满意度的关键。
|
24天前
|
存储 JavaScript 开发者
探索鸿蒙新世界:ArkUI框架实战指南,解锁HarmonyOS应用UI设计的无限可能!
【10月更文挑战第19天】ArkUI框架是华为鸿蒙系统中用于开发用户界面的核心工具,支持ArkTS和eTS两种开发语言。本文介绍了ArkUI的基本概念、组件使用、布局管理和状态管理,通过示例代码帮助开发者轻松构建美观、高效的跨设备UI。
68 3
|
11天前
|
Java 测试技术 持续交付
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
本文重点讲解如何搭建App自动化测试框架的思路,而非完整源码。主要内容包括实现目的、框架设计、环境依赖和框架的主要组成部分。适用于初学者,旨在帮助其快速掌握App自动化测试的基本技能。文中详细介绍了从需求分析到技术栈选择,再到具体模块的封装与实现,包括登录、截图、日志、测试报告和邮件服务等。同时提供了运行效果的展示,便于理解和实践。
46 4
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
|
17天前
|
前端开发 数据管理 测试技术
前端自动化测试:Jest与Cypress的实战应用与最佳实践
【10月更文挑战第27天】本文介绍了前端自动化测试中Jest和Cypress的实战应用与最佳实践。Jest适合React应用的单元测试和快照测试,Cypress则擅长端到端测试,模拟用户交互。通过结合使用这两种工具,可以有效提升代码质量和开发效率。最佳实践包括单元测试与集成测试结合、快照测试、并行执行、代码覆盖率分析、测试环境管理和测试数据管理。
33 2
|
17天前
|
Web App开发 定位技术 iOS开发
Playwright 是一个强大的工具,用于在各种浏览器上测试应用,并模拟真实设备如手机和平板。通过配置 `playwright.devices`,可以轻松模拟不同设备的用户代理、屏幕尺寸、视口等特性。此外,Playwright 还支持模拟地理位置、区域设置、时区、权限(如通知)和配色方案,使测试更加全面和真实。例如,可以在配置文件中设置全局的区域设置和时区,然后在特定测试中进行覆盖。同时,还可以动态更改地理位置和媒体类型,以适应不同的测试需求。
Playwright 是一个强大的工具,用于在各种浏览器上测试应用,并模拟真实设备如手机和平板。通过配置 `playwright.devices`,可以轻松模拟不同设备的用户代理、屏幕尺寸、视口等特性。此外,Playwright 还支持模拟地理位置、区域设置、时区、权限(如通知)和配色方案,使测试更加全面和真实。例如,可以在配置文件中设置全局的区域设置和时区,然后在特定测试中进行覆盖。同时,还可以动态更改地理位置和媒体类型,以适应不同的测试需求。
17 1
|
18天前
|
前端开发 JavaScript 数据可视化
前端自动化测试:Jest与Cypress的实战应用与最佳实践
【10月更文挑战第26天】前端自动化测试在现代软件开发中至关重要,Jest和Cypress分别是单元测试和端到端测试的流行工具。本文通过解答一系列问题,介绍Jest与Cypress的实战应用与最佳实践,帮助开发者提高测试效率和代码质量。
28 2
|
24天前
|
JavaScript API 开发者
掌握ArkTS,打造HarmonyOS应用新视界:从“Hello World”到状态管理,揭秘鸿蒙UI开发的高效秘诀
【10月更文挑战第19天】ArkTS(ArkUI TypeScript)是华为鸿蒙系统中用于开发用户界面的声明式编程语言,结合了TypeScript和HarmonyOS的UI框架。本文介绍ArkTS的基本语法,包括组件结构、模板和脚本部分,并通过“Hello World”和计数器示例展示其使用方法。
51 1
|
9天前
|
前端开发 Android开发 UED
安卓应用开发中的自定义控件实践
【10月更文挑战第35天】在移动应用开发中,自定义控件是提升用户体验、增强界面表现力的重要手段。本文将通过一个安卓自定义控件的创建过程,展示如何从零开始构建一个具有交互功能的自定义视图。我们将探索关键概念和步骤,包括继承View类、处理测量与布局、绘制以及事件处理。最终,我们将实现一个简单的圆形进度条,并分析其性能优化。
|
15天前
|
NoSQL 测试技术 Go
自动化测试在 Go 开源库中的应用与实践
本文介绍了 Go 语言的自动化测试及其在 `go mongox` 库中的实践。Go 语言通过 `testing` 库和 `go test` 命令提供了简洁高效的测试框架,支持单元测试、集成测试和基准测试。`go mongox` 库通过单元测试和集成测试确保与 MongoDB 交互的正确性和稳定性,使用 Docker Compose 快速搭建测试环境。文章还探讨了表驱动测试、覆盖率检查和 Mock 工具的使用,强调了自动化测试在开源库中的重要性。