Appium 手机自动化

Appium 用途和特点

Appium 简介

Appium 是一个移动 App (手机应用)自动化工具。

手机 APP 自动化有什么用?

  • 自动化完成一些重复性的任务,比如:微信客服机器人
  • 爬虫:就是通过手机自动化爬取信息。为什么不通过网页、HTTP 爬取呢?有的系统没有网页,也不方便通过 HTTP 爬取。
  • 自动化测试:很多企业里面有这样的需求

Appium 自动化方案的特点

  • 开源免费
  • 支持多个平台:iOS (苹果)、安卓 App 的自动化都支持。
  • 支持多种类型的自动化
    • 支持 苹果、安卓 应用 原生界面 的自动化
    • 支持 应用 内嵌 WebView 的自动化
    • 支持 手机浏览器 中的 web 网站自动化
    • 支持 flutter 应用的自动化
  • 支持多种编程语言:像 Selenium 一样, 可以用多种编程语言调用它开发自动化程序。

Appium 自动化原理

Appium 自动化原理图

原理图

包含了 3 个主体部分 : 自动化程序、Appium Server、移动设备

自动化程序

  • 自动化程序是由我们来开发的,实现具体的手机自动化功能。
  • 要发出具体的指令控制手机,也需要使用客户端库
  • 和 Selenium 一样,Appium 组织也提供了多种编程语言的客户端库,包括 Java,Python,JS, Ruby 等,方便不同编程语言的开发者使用。
  • 我们需要安装好客户端库,调用这些库,就可以发出自动化指令给手机。

Appium Server

Appium Server 是 Appium 组织开发的程序,它负责管理手机自动化环境,并且转发自动化程序的控制指令给手机,并且转发手机给自动化程序的响应消息。

手机设备

  • 我们这里说的手机设备,其实不仅仅是手机,包括所有苹果、安卓的移动设备,比如:手机、平板、智能手表等。
  • 为了直观方便的讲解,这里我们简称: 手机
  • 当然手机上也包含了我们要自动化控制的手机应用 APP。
  • 手机设备为什么能接收并且处理自动化指令呢?
    • 因为,Appium Server 会在手机上安装一个自动化代理程序, 代理程序会等待自动化指令,并且执行自动化指令

原理示例

比如:要模拟用户点击界面按钮,Appium 自动化系统的流程是这样的:

  • 自动化程序调用客户端库相应的函数, 发送 点击元素 的指令(封装在 HTTP 消息里)给 Appium Server
  • Appium Server 再转发这个指令给手机上的自动化代理
  • 手机上的自动化代理接收到指令后,调用手机平台的自动化库,执行点击操作,返回点击成功的结果给 Appium Server
  • Appium Server 转发给自动化程序
  • 自动化程序了解到本操作成功后,继续后面的自动化流程

其中,自动化代理控制,使用的什么库来实现自动化的呢?

  • 如果测试的是苹果手机, 用的是苹果的 XCUITest 框架 (IOS9.3 版本以后)
  • 如果测试的是安卓手机,用的是安卓的 UIAutomator 框架 (Android4.2 以后)
  • 这些自动化框架提供了在手机设备上运行的库,可以让程序调用这些库,像人一样自动化操控设备和 APP,比如:点击、滑动,模拟各种按键消息等。

Appium 自动化环境搭建 (安卓)

环境搭建所需软件下载

安装 client 编程库

因为我们介绍 Python 语言开发,所以当然是用 pip 安装,安装命令如下

1
pip install appium-python-client

安装 Appium Server

  • Appium Server 是用 Node.js 运行的,基于 js 开发出来的。
  • Appium 组织为了方便大家安装使用,制作了一个可执行程序 Appium Desktop,把 Node.js 运行环境、Appium Server 和一些工具打包在里面了,只需要简单的下载安装就可以了。
  • 可以从上面给出的下载链接下载安装:Appium-windows-1.21.0.exe

安装 JDK

  • 本教程主要讲解安卓 APP 的自动化,必须要安装安卓 SDK(后面会讲到),而安卓 SDK 需要 JDK 环境。
  • 可以从上面给出的下载链接下载安装:jdk-16.0.1_windows-x64_bin.exe
  • 安装好之后,还需要添加一个环境变量 JAVA_HOME ,指定值为 JDK 安装目录,比如:
    1
    JAVA_HOME	C:\Program Files\Java\jdk-16.0.1

安装 Android SDK

  • 对于安卓 App 的自动化,Appium Server 是需要 Android SDK 的。
  • 因为要用到里面的一些工具,比如要执行命令设置手机、传送文件、安装应用、查看手机界面等。
  • 可以从上面给出的下载链接下载安装 (Android 11):android-sdk-windows.rar
  • 安装教程
  • 解压完成后,需要配置一下,添加一个环境变量 ANDROID_HOME ,设置值为 SDK 包解压目录,比如:
    1
    ANDROID_HOME	D:\Program Files (x86)\android-sdk-windows
  • 另外,还推荐大家配置环境变量 PATH ,加入 adb 所在目录,如:
    1
    PATH	D:\Program Files (x86)\android-sdk-windows\platform-tools

    ⚠️注意:是 添加 该目录到环境变量 PATH 中, !!!不是替换!!! ,否则会导致系统命令都找不到的严重后果。

连接手机

上述的软件环境都准备好以后,要自动化手机 APP,需要:

  • 在你运行程序的电脑上用 USB 线连接上你的安卓手机
  • 进入 手机设置 -> 关于手机 ,不断点击 版本号 菜单(7 次以上)
  • 退出到上级菜单,在开发者模式中,启动 USB 调试

如果手机连接 USB 线后,手机界面弹出类似如下提示,请选择 允许 USB 调试。

允许USB调试

⚠️注意:

  • 有的手机系统,可能需要一些额外的选项需要设置好。
  • 比如,有的手机,开发者选项里需要打开 允许通过USB安装应用 等。
  • 总之,给 USB 开发调试尽可能方便的控制手机。

连接好以后,打开命令行窗口, 执行 adb devices -l 命令来列出连接在电脑上的安卓设备。
如果输出类似如下的内容:

1
2
List of devices attached
868386b0 device product:cas model:M2007J1SC device:cas transport_id:1

表示电脑上可以查看到连接的设备,就可以运行自动化程序了。

代码示例

使用 Appium 自动化的打开 B 站应用

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
44
45
46
import time

from appium import webdriver
from appium.webdriver.extensions.android.nativekey import AndroidKey

desired_capabilities = {
'platformName': 'Android', # 被检测手机是安卓
'platformVersion': '11', # 手机安卓版本
'deviceName': 'xxx', # 设备名,安卓手机可以随意填写
'appPackage': 'tv.danmaku.bili', # 启动App package名称
'appActivity': '.ui.splash.SplashActivity', # 启动Activity名称
'unicodeKeyboard': True, # 使用自带输入法,输入中文时填True
'resetKeyboard': True, # 执行完程序恢复原来输入法
'noReset': True, # 不要重置App
'newCommandTimeout': 6000,
'automationName': 'UiAutomator2',
}

# 连接Appium Server,初始化自动化环境
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_capabilities)
# 设置缺省等待时间
driver.implicitly_wait(5)
# 如果有“青少年保护”界面,点击“我知道了”
try:
iknow = driver.find_element_by_id('text3')
except Exception as e:
print(e)
print('======没有出现“青少年保护”保护界面======')
else:
iknow.click()
# 根据id定位搜索位置框,点击
driver.find_element_by_id('expand_search').click()
time.sleep(1)
# 根据id定位搜索输入框,输入
driver.find_element_by_id('search_src_text').send_keys('白月黑羽')
# 输入回车键,确定搜索
driver.press_keycode(AndroidKey.ENTER)
# 选择(定位)所有视频标题
eles = driver.find_elements_by_id('title')
for ele in eles:
# 打印标题
print(ele.text)

input('======Press any key to exit======')
driver.quit()

运行代码前,先要 运行Appium Desktop报错参考链接

查找 appPackage 和 appActivity

没有 apk 安装包

如果你应用已经安装在手机上了,可以直接打开手机上该应用,进入到你要操作的应用界面,然后在 命令提示符 界面下执行以下代码:

1
adb shell dumpsys activity recents | find "intent={"

⚠️注意:如果在 “PowerShell” 界面下执行该语句,可能会报错。

结果显示如下,最近的几个 activity 信息:

1
2
3
4
intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=tv.danmaku.bili/.ui.splash.SplashActivity}
intent={act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10800100 cmp=com.miui.home/.launcher.Launcher}
intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=io.legado.app.release/io.legado.app.ui.welcome.WelcomeActivity}
intent={act=android.settings.SETTINGS flg=0x14000000 cmp=com.android.settings/.MiuiSettings}

其中第一行就是当前的应用,我们特别关注最后

1
cmp=tv.danmaku.bili/.ui.splash.SplashActivity
  • 应用的 appPackage 名称就是:tv.danmaku.bili
  • 应用的 appActivity 名称就是:.ui.splash.SplashActivity

有 apk 安装包

如果你已经获取到了 apk 安装包,需要首先切换到 aapt.exe 目录下,如:D:\Program Files (x86)\android-sdk-windows\build-tools\30.0.3,然后在 命令提示符 界面下运行以下代码:

1
aapt.exe dump badging C:\Users\suyin\Desktop\iBiliPlayer-bili.apk | find "package: name="

输出信息中,就有 appPackage 名称

1
package: name='tv.danmaku.bili' versionCode='6262000' versionName='6.26.2' compileSdkVersion='30' compileSdkVersionCodename='11'

命令提示符 界面下运行以下代码:

1
aapt.exe dump badging C:\Users\suyin\Desktop\iBiliPlayer-bili.apk | find "launchable-activity"

输出信息中,就有应用的启动 appActivity

1
launchable-activity: name='tv.danmaku.bili.ui.splash.SplashActivity'  label='' icon=''

⚠️注意事项:

  • 一定要记得首先切换目录到 aapt.exe 目录下
  • 要在 命令提示符 界面运行以上代码
  • 如果在 “PowerShell” 界面下执行该语句,可能会报错
  • C:\Users\suyin\Desktop\iBiliPlayer-bili.apk 是你下载的 apk 安装包的路径

定位元素

代码规则

和 Selenium Web 自动化一样,要操作界面元素,必须先定位 (选择) 元素。

  • find_element_by_XXX 方法,返回符合条件的第一个元素,找不到抛出异常
  • find_elements_by_XXX 方法,返回符合条件的所有元素的列表,找不到返回空列表
  • 通过 WebDriver 对象调用这样的方法,查找范围是整个界面
  • 通过 WebElement 对象调用这样的方法,查找范围是该节点的子节点

界面元素查看工具

  • 简介

    • 做 Selenium Web 自动化的时候,要找到元素,我们是通过浏览器的开发者工具栏来查看元素的特性,根据这些特性 (属性和位置),来定位元素
    • Appium 要自动化手机应用,同样需要工具查看界面元素的特征
    • 常用的查看工具是: Android SDK 包中的 uiautomateviewer 和 Appium Desktop 中的 Appium Inspector
  • uiautomateviewer

    • 安卓查看 APP 界面元素,最常用的就是 Android SDK 中的工具 uiautomateviewer ,它在 SDK 目录中的 tools\bin 目录中,如:D:\Program Files (x86)\android-sdk-windows\tools\bin
    • 和 Selenium 一样,我们要定位选择元素,也是根据元素的特征,包括
      • 元素的属性
      • 元素的相对位置 (相对父元素、兄弟元素等)
    • 使用 uiautomateviewer 报错,解决方案链接
    • uiautomateviewer 使用视频教程:前 13 分钟(0-13 分钟)
  • Appium Inspector

    • Appium Desktop 中的 Appium Inspector 也可以查看元素
    • 它的一个优点是可以直接验证选择表达式是否能定位到元素
    • Appium Inspector 使用视频教程:后 13 分钟(13-26 分钟)

定位元素的方法

  • 根据 ID

    • 在 Selenium Web 自动化教程里,我们说过,如果能根据 ID 选择定位元素,最好根据 ID,因为通常来说 ID 是唯一的,所以根据 ID 选择效率高。
    • 在安卓应用自动化的时候,同样可以根据 ID 查找。
    • 但是这个 ID ,是安卓应用元素的 resource-id 属性
    • 使用如下代码
      1
      driver.find_element_by_id('expand_search')
  • 根据 CLASS NAME

    • 安卓界面元素的 class 属性其实就是根据元素的类型,类似 web 里面的 tagname, 所以通常不是唯一的。
    • 通常,我们根据 class 属性来选择元素, 是要选择多个而不是一个。
    • 当然如果你确定要查找的界面元素的类型在当前界面中只有一个,就可以根据 class 来唯一选择。
    • 使用如下代码
      1
      driver.find_elements_by_class_name('android.widget.TextView')
  • 根据 ACCESSIBILITY ID

    • 元素的 content-desc 属性是用来描述该元素的作用的。
    • 如果要查询的界面元素有 content-desc 属性,我们可以通过它来定位选择元素。
    • 使用如下代码
      1
      driver.find_element_by_accessibility_id('搜索查询')
  • Xpath

    • Appium 也支持通过 Xpath 选择元素。
    • 但是其可靠性和性能不如 Selenium Web 自动化。因为 Web 自动化对 Xpath 的支持是由浏览器实现的,而 Appium Xpath 的支持是 Appium Server 实现的。毕竟,浏览器产品的成熟度比 Appium 要高很多。
    • 当然,Xpath 是标准语法,所以这里表达式的语法规则和以前学习的 Selenium 里面 Xpath 的语法是一样的,比如:
      1
      driver.find_element_by_xpath('//ele1/ele2[@attr="value"]')
    • 注意:
      • Selenium 自动化中, Xpath 表达式中每个节点名是 html 的 tagname。
      • 但是在 Appium 中, Xpath 表达式中每个节点名是元素的 class 属性值。
      • 比如:要选择所有的文本节点,就使用如下代码:
        1
        driver.find_element_by_xpath('//android.widget.TextView')
  • 安卓 UI Automator

    • 根据 id,classname, accessibilityid,xpath,这些方法选择元素,其实底层都是利用了安卓 uiautomator 框架的 API 功能实现的。
    • 参考这里的谷歌安卓官方文档介绍
    • 也就是说,程序的这些定位请求,被 Appium server 转发给手机自动化代理程序,就转化为 uiautomator 里面相应的定位函数调用。
    • 其实,我们的自动化程序,可以直接告诉手机上的自动化代理程序,让它调用 UI Automator API 的 java 代码,实现最为直接的自动化控制。
    • 主要是通过 UiSelector 这个类里面的方法实现元素定位的,比如:
      1
      2
      3
      code = 'new UiSelector().text("热门").className("android.widget.TextView")'
      ele = driver.find_element_by_android_uiautomator(code)
      ele.click()

      就是通过 text 属性和 className 的属性两个条件来定位元素

    • UiSelector 里面有些元素选择的方法可以解决前面解决不了的问题,比如:
      • text 方法:可以根据元素的文本属性查找元素
      • textContains:根据文本包含什么字符串
      • textStartsWith:根据文本以什么字符串开头
      • textmartch:可以使用正则表达式 选择一些元素,如下
        1
        code = 'new UiSelector().textMatches("^我的.*")'
    • UiSelector 的 instanceindex 也可以用来定位元素,都是从 0 开始计数, 他们的区别:
      • instance 是匹配的结果所有元素里面的第几个元素
      • index 则是其父元素的几个节点,类似 Xpath 里面的 *[n]
    • UiSelector 的 childSelector 可以选择后代元素,比如
      1
      2
      3
      code = 'new UiSelector().resourceId("tv.danmaku.bili:id/recycler_view").childSelector(new UiSelector().className("android.widget.TextView"))'

      ele = driver.find_element_by_android_uiautomator(code)

      注意:childSelector 后面的引号要框住整个子 uiSelector 的表达式;目前有个 bug:只能找到符合条件的第一个元素,参考 appium 在 github 上的 issues

界面操作和 adb 命令

界面操作

  • click 点击

    最常见的操作之一,使用 WebElement 对象的 click 方法,示例代码中已展示,在此不再赘述

  • tap 点按

    • WebElement 对象的 tap 方法和 click 类似,都是点击界面
    • 但是最大的区别是, tap 是 针对坐标 而不是针对找到的元素
    • 为了保证自动化代码在所有分辨率的手机上都能正常执行,我们通常应该使用 click 方法
    • 但有的时候,我们难以用通常的方法定位元素, 可以用这个 tap 方法,根据坐标来点击
    • 既然 tap 是用坐标来点击界面的,我们怎么知道这个元素的坐标呢?
      • 大家还记得用 inspect 查看该元素的属性中,有一个 bounds 属性吗?
      • 它就是表示元素的左上角,右下角坐标的 坐标
      • 我们还可以使用 UIAutomatorviewer 直接光标移动,看右边的属性提示
    • tap 方法可以像这样进行调用
      1
      driver.tap([(850,1080)],300)
    • 它有两个参数:
      • 第一个参数是个列表,表示点击的坐标
        • 注意最多可以有 5 个元素,代表 5 根手指点击 5 个坐标。所以是 list 类型
        • 如果我们只要模拟一根手指点击屏幕,list 中只要一个元素就可以了
      • 第二个参数表示 tap 点按屏幕后停留的时间
        • 如果点按时间过长,就变成了长按操作了
  • 输入

    最常见的操作之一, 使用 WebElement 对象的 send_keys 方法, 示例代码中已展示,在此不再赘述

  • 获取界面文本信息

    可以通过 WebElement 对象的 .text 属性获取该对象的文本信息,示例代码中已展示,在此不再赘述

  • 滑动

    • 我们做移动 app 测试的时候,经常需要滑动界面,怎么模拟滑动呢? WebDriver 对象的 swipe 方法,就提供了这个功能,比如:
      1
      driver.swipe(start_x=x, start_y=y1, end_x=x, end_y=y2, duration=800)
    • 前面 4 个参数是:滑动起点和终点的 x、y 坐标
    • 第 5 个参数 duration 是滑动从起点到终点坐标所耗费的时间
    • ⚠️注意:这个时间非常重要,在屏幕上滑动同样的距离,如果时间设置的很短,就是快速的滑动
    • 比如:一个翻动新闻的界面,快速的滑动,就会是扫动的动作,会导致内容 惯性 滚动很多
  • 按键

    • 前面的示例代码中已经使用过调用 press_keycode 方法,就能模拟按键动作,包括安卓手机的实体按键和键盘按钮
    • 如下代码所示
      1
      2
      3
      4
      from appium.webdriver.extensions.android.nativekey import AndroidKey

      # 输入回车键,确定搜索
      driver.press_keycode(AndroidKey.ENTER)
    • 按键定义,参考文档
  • 长按、双击、移动

    • Appium 的 TouchAction 类提供了更多的手机操作方法,比如:长按、双击、移动
    • 参考源代码中的注释
    • 比如,下面就是一个长按的例子
      1
      2
      3
      4
      5
      from appium.webdriver.common.touch_action import TouchAction
      # ...
      actions = TouchAction(driver)
      actions.long_press(element)
      actions.perform()
  • 查看通知栏

    • 打开通知栏
      • 安卓手机, 查看通知栏的动作可以是从屏幕顶端下滑来查看通知
      • 我们刚刚学过滑动,感兴趣的朋友可以自己试试,关键是要找对滑动的起始点和滑动距离
      • 更方便的,我们可以使用如下代码,直接打开通知栏
        1
        driver.open_notifications()
      • 通知栏里面的元素,自动化的方法和前面介绍的 App 界面元素自动化是一样的
    • 收起通知栏
      • 收起通知栏,可以使用前面介绍的模拟按键, 发出返回按键的方法

adb 命令

  • adb 简介

    • 这里我们给大家介绍一个 android sdk 里面的命令行工具 adb,adb 全称 Android Debug Bridge,这个 adb 使用非常广泛,它可以与 Android 手机设备进行通信,它可进行各种设备操作。
    • 比如: 安装应用和调试应用,传输文件,甚至登录到手机设备上 shell 的进行访问,就像远程登录一样
    • 这个 adb 在 sdk 的 platform-tools 目录下面, 请大家确保路径在 path 环境变量中
    • Appium 对 Android 的自动化就非常依赖这个 adb 工具。 执行自动化过程中,有很多内部操作,比如获取设备信息,传送文件到手机,安装 apk,启动某些程序等,都是通常这个 adb 实现的
    • 大家想想我们学习了 adb 命令,对我们的自动化程序有什么用例呢?
    • 既然这是个命令,就可以使用 Python 的 os.system() 或者 subprocess 来自动化调用它,完成我们的各种自动化需求
    • 比如,我们自动化过程中,可能需要截屏手机,并且下载到指定目录中,就可以在我们的 Python 程序中这样写
      1
      2
      3
      import os

      os.system('adb shell screencap /sdcard/screen3.png && adb pull /sdcard/screen3.png')
    • 特别是,还可以通过 adb 使用 am (activity manager) 和 pm (package manager) 两个工具, 可以启动 Activity、强行停止进程、广播 intent、修改设备屏幕属性、列出应用、卸载应用等
    • adb 命令官方文档
  • 查看连接的设备

    1
    adb devices -l
  • 列出文件和传输文件

    • 查看目录
      1
      adb shell ls /sdcard
    • 上传
      1
      adb push wv.apk /sdcard/wv.apk
    • 下载
      1
      adb pull /sdcard/new.txt
  • 截屏

    1
    adb shell screencap /sdcard/screen.png

    截屏后的文件存在手机上,可以使用 adb pull 下载下来

  • shell

    • 登录到手机设备上 shell 的进行访问,就像远程登录一样,可用来在连接的设备上运行各种命令
    • 大家可以执行一下 adb shell 然后执行各种安卓支持的 Linux 命令,比如 ps、netstat、netstat -an|grep 4724、 pwd、 ls 、cd 、rm 等
    • 执行 quit 退出 shell

内嵌网页自动化

准备工作

内嵌网页的混合 App

  • 很多移动 App 都是 Hybrid(混合) 应用。混合应用主要是指它的一部分是原生界面和代码,而另一部分是内嵌网页 ,现在基本上需要打开网页浏览的 app 都是混合 app,比如微信、支付宝等。微信的 sms 界面是原生代码实现的,而打开某个朋友圈,或者别人发来的的链接部分则是 web 部分。
  • App 中的内嵌的展示网页内容的模块,我们称之为 webview
  • 我们自动化的时候如果需要操作内嵌 webview 中的网页内容,该怎么做呢?

修改编译 App

  • 前面讲过,Appium 的原则是不修改应用本身,就可以对应用进行自动化。但是,这里要违背一下 Appium 的原则,要对 App 内嵌网页进行自动化,首先要请开发人员修改源代码,保证对 webview 对象加入 setWebContentsDebuggingEnabled 的调用

  • 安卓应用,修改 java 代码,如下所示

    1
    2
    3
    4
    5
    6
    7
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 找到 webview 对象
    WebView myWebView = (WebView) findViewById(R.id.jcywebview);
    // 添加打开 webview内容debug开关
    myWebView.setWebContentsDebuggingEnabled(true);
    };
  • 然后,编译出一个支持自动化的版本,否则无法对 webview 中的内容进行自动化操作

用 Chrome 查看 App 内嵌网页

  • 简介

    • 编译产生了支持自动化的 App,接下来我们就可以安装到手机上,进行内嵌网页的自动化了
    • App 内嵌网页的自动化,也是使用 Selenium。 和电脑上自动化 Chrome 浏览器本质是一样的,因为安卓手机内嵌的 webview 基本上就是手机版的 Chrome 浏览器,但是,因为网页内容在手机中呈现,不能像电脑浏览器那样,打开开发者工具,查看元素内容
    • 那我们怎么查看元素的属性特征,来定位选择元素呢?
    • 这里面要分两种情况:
      • webview 不依赖 App 环境
      • webview 依赖 App 环境
  • webview 不依赖 App 环境

    这种情况下 App 的内嵌 webview 和 native部分是没有交互 的,webview 只是打开一个固定的网址(一般是该公司的手机网址)而已,这种情况,我们要查看 webview 内容非常简单,因为其实和手机没有关系,直接用 Chrome 浏览器 F12 里面的手机模式打开对应的网页,即可,如图所示:

  • webview 依赖 App 环境

    • 这种情况下 App 的内嵌 webview 和 native部分是有交互 的, 它必须在 app 中才能运行,这种情况,我们直接打开网页 url 运行不起来(或者说运行结果和在 app 中不同), 甚至,我们根本就不知道网页的 url。怎么办?
    • 这时候,我们可以通过 Chrome 浏览器的远程调试功能,⚠️注意:这种方法,一定要你的网络环境能访问谷歌!!!
    • 假设你的网络环境是可以连接谷歌的:
      • 首先,确保我们的应用运行
      • 然后,打开 Chrome 浏览器,地址栏输入 chrome://inspect ,出现如下所示界面
      • ⚠️注意:上图红框内是 webview 版本。有的手机比较老 ,webview 版本也比较老的,就会有问题, 尽量使用新手机进行自动化
      • 点击 inspect 即可查看,如下图所示
      • 具体操作,可以参考官方文档

自动化代码

  • 请大家运行前文下载的 wv App,界面如下
    wv
  • 这个 App 内置了一个 webview,这个 webview 缺省就是打开百度的网址,和 native 代码没有交互, 完全可以用浏览器直接以手机模式查看网页元素
  • 我们假设要做的就是在这个应用,进入 webview 里面的百度,搜索关键字 白月黑羽 , 我们看看代码怎么实现。前面已经说过,webview 自动化代码和电脑上浏览器的自动化基本差不多,但是有一点要注意:手机 App 中 webview 里面的网页内容, 是在一个独立于应用 native 部分的环境里面的;而缺省情况下,find_element_by_xxx 这样的代码选择元素,Appium 只会在 native 部分的界面寻找元素,肯定找不到元素。
  • Appium 把一个界面环境称之为一个 context ,native 部分的 context 名字为 NATIVE_APP,而 webview 部分的 context 则为 WEBVIEW_XXX (XXX 部分是应用的 app package 名)
  • 我们怎么查看当前有哪些 context 呢?
    • 我们的代码通过 driver 对象的 contexts 属性来获取,也就是 driver.contexts
    • driver 对象的 current_context 属性对应当前的 context 对象
  • 大家可以打开自动化代码, 添加如下内容
    1
    print(driver.contexts)
  • 执行一下,解释一下,可以发现结果如下
    1
    2
    3
    4
    [
    'NATIVE_APP',
    'WEBVIEW_com.example.jcy.wvtest'
    ]
  • 我们的应用中, webview 的 context 就是 WEBVIEW_com.example.jcy.wvtest,而当前的 context 是 'NATIVE_APP', 所以当前的自动操作都是在 native context 里面的进行的,要对该 webview 里面的网页内容进行自动化操作,必须先将当前的 context 切换为 webview 的 context,怎么切呢?
    • 使用 switch_to.context
      1
      driver.switch_to.context('WEBVIEW_com.example.jcy.wvtest')
  • 好,现在我们修改代码,如下所示
    1
    2
    3
    4
    5
    driver.switch_to.context('WEBVIEW_com.example.jcy.wvtest')

    driver.find_element_by_id('index-kw').send_keys('白月黑羽')

    driver.find_element_by_id('index-bn').click()
  • 执行一下,发现可以自动化了,那么怎么切换回 native app 进行自动化呢?
    • 当然是继续使用 switch_to.context ,如下
      1
      driver.switch_to.context('NATIVE_APP')
  • 大家可以查看一下,app 界面上 B站QQ 的 id 分别为:navigation_bili 和 navigation_qq
  • 如果我们想再切换回 native 部分,分别点击底部导航栏 B站 按钮和 QQ 按钮,就可以修改代码如下
    1
    2
    3
    4
    driver.switch_to.context('NATIVE_APP')
    driver.find_element_by_id('navigation_bili').click()
    time.sleep(2)
    driver.find_element_by_id('navigation_qq').click()

手机浏览器网页自动化

  • 有的公司开发了手机版网站,直接用手机浏览器打开的,比如:xiaomi、京东等,并不是手机 App,这种手机网页,我们怎么程序自动化呢?
  • 首先,必须在手机上安装谷歌浏览器。以百度为例,首先启动 Appium Desktop。然后,我们的自动化程序代码如下所示:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    desired_caps = {}
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '7'
    desired_caps['deviceName'] = 'test'
    desired_caps['browserName'] = 'Chrome'
    desired_caps['newCommandTimeout'] = 6000
    driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)

    driver.implicitly_wait(10)
    try:

    driver.get('http://www.baidu.com')

    driver.find_element_by_id('index-kw').send_keys('白月黑羽')

    driver.find_element_by_id('index-bn').click()

    except:
    print(traceback.format_exc())

webview、驱动版本配套

  • 问题

    • 大家知道,基于 Appium 的安卓应用自动化时,如果要自动化的 app 里面内嵌网页内容, Appium 会使用基于 Selenium 的自动化机制
    • 安卓 app 里面的网页,基本上都是使用手机系统上的 webview 去显示的,安卓 webview 可以看成是手机上的 Chrome 浏览器精简版
    • 我们知道 Selenium 自动化 Chrome 浏览器,需要使用相应版本的浏览器驱动,Chrome 浏览器和 chromedriver 的配套关系可以在下面这个链接查看到:Chrome 浏览器和 chromedriver 版本配套表
    • Appium Desktop 里面内置了用于 webview 自动化的 chromedriver。 比较新的 Appium server, 内置 chromedriver 也是比较新的,而自动化的手机如果比较老, 那么手机里面的 webview 版本也会比较老,这是就会出现和新版本的 Appium Desktop 里面的 chromedriver 不匹配的问题。 自动化程序运行时, appium 会报类似如下错误
      error
    • 上面的错误,就是说你的手机 webview 版本是 58, 而你的 Appium Desktop 里面的 chromedriver 版本又是比较新的, 对这个老版本的 webview 就不匹配了
  • 解决方案

    • 这时,我们可以根据配套表
      配套表
    • 下载对应的老版本的 chromedriver 2.31,然后设置 Appium Desktop 使用这个版本的 chromedriver
    • 如下图所示,先点击 Advanced 设置项,然后在下图位置写上你的老版本的 chromedriver 的路径
      步骤1
      步骤2