1、背景

最近开始写UI自动化测试用例,发现很多UI自动化知识不用的时候很容易遗忘,使用的时候又需要快速捡起,在回顾的过程中把相关知识总结整理了出来,希望作为一个小小的知识百科分享,方便快速查阅。

2、Appium工作原理

Android 手动运行 uiautomator_Android

  • Appium的核心其实就是一个暴露了一系列REST API的server
  • 这个server的功能很简单:监听一个端口(4723),接收由客户端发送过来的命令command,翻译这些command
  • 把这些command转换成移动设备可以理解的形式发送给移动设备,调用设备上内置的自动化框架api去执行操作
  • 移动设备执行完这些command后把执行结果返回给appium server,最后Appium server再把执行后的结果返回给client客户端

Android 手动运行 uiautomator_自动化_02

2.1 手机端运行原理如下

  • appium使用adb向手机/模拟器部署Bootstrap.jar
  • Bootstrap.jar接收appium发过来的指令
  • Bootstrap.jar再次将命令发给uiautomator
  • uiautomator再次执行命令

2.1.1 一个完整的请求过程

  • 收到http请求
  • 创建会话
  • 确认当前是否有连接的设备,并且确认是否匹配安卓版本
  • 确认设备上有没有指定包名的app
  • 往设备上安装appium自己的 setting apk包。若已安装,需要确认apk版本是否匹配。
  • appium会往设备上推送一个appiumbootstrap.jar包。–java代码

运行jar包,在手机上启动一个ip+端口。接收来自appium server的请求,调用手机上的自动化框架执行操作。

2.1.2 建立连接完整步骤

准备工作:

  • 启动appium程序
  • 至少有一个设备是能够识别到的 adb devices
  • 获取应用包名appPackage和appActivity

2.2 aapt命令:apk获取应用包名appPackage和appActivity

  • aapt目录:/adt-bundle-mac-x86_64-20140624/sdk/build-tools/android-4.4W/aapt
  • 命令用法:aapt dump badging apk应用名
  • 示例:aapt dump badging D:\BaiduNetdiskDownload\Future-release-2019.apk
  • launchable activity后就是 appActivity

2.2.1 准备启动参数:告诉appium,要打开哪个设备上的哪个app

原理:

  • appium desktop服务同时支持androidios,androidios有自带的自动化框架
  • AndroidiOS有很多的版本且版本之间存在自动化框架上的差异,所有代码当中必须告诉appium
  • 你是要在 哪个平台的哪个版本上,对哪个app进行操作。

启动参数获取地址:http://appium.io/docs/en/writing-running-appium/caps/#general-capabilities

  • 1.告诉平台 android/ios
  • 2.系统版本号
  • 3.设备名称 需要这个参数但是没有实际用处
  • 4.包名
  • 5.appActivity入口页面
  • 6.noReset:不重置应用的数据,假如现在已经登录上app.如果是noReset=false,那么appium执行的时候会将应用的数据清除,反之noReset=true,appium执行时候不会清掉数据进行测试。默认是noReset=false

2.2.2 启动appium server程序 查看端口号

2.2.3 连接appium server,把端口替换为appium启动后的端口号,开启appium server

2.2.4 运行代码

3、Appium安装五大件

  • appium desktop http://appium.io
  • appium-pythonpip install Appium-Python-Client
  • ADT工具包 安装AndroidStudio版本然后配置环境变量
    参考此篇文章
  • 模拟器/真机 真机需要在开发者选项中开启USB调试模式
  • JDK 安装JDK1.8,配置环境变量

4、app元素定位三个工具

元素定位出错后我们首先要检查:

  • adb能识别到设备吗
  • 定位时设备有被其他程序占用吗(即便是appium server在占用也不可以,必须等appium server停止运行才可以定位到)
  • ADT的版本正确吗

5、每一种工具正确定位的前提:

  • 通过 adb devices命令能够识别到至少一个android设备;
  • 设备当前没有被任何其它的应用程序所占用;

5.1 第一种:python版uiAutomator2中的weditor

python语言实现的一个app自动化测试框架。

安装

安装uiAutomator2:pip install -U uiautomator2
初始化命令(往手机上推送apk包):python -m uiautomator2 init
安装定位工具weditor: pip install -U weditor
安装完成之后:命令行运行:weditor --help确认是否安装成功。

使用

  1. 命令行输入:python -m weditor 会自动打开在浏览器当中打开一个页面
  2. 选择Android,输入设备序列号,点击connect。点击Dump Hierarchy,就可以看到元素的信息了。
  3. 如果xpath定位使用了sourceid代表sourceid是唯一的可以直接用find_element_by_id

5.2 第二种:android ADT自带工具:uiAutormatorViewer

android 4.2以上的版本,自带的自动化测试框架是uiAutomator
uiAutomatorViewer就是它的元素定位工具了

缺点

不支持jdk1.8以上的版本。所以为了启动成功,要安装jdk1.8的版本。
mac下若安装了jdk11和jdk1.8,需要编辑uiautomatorviewer文件,找到javaCmd="java",
修改为 javaCmd="/Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/bin/java"/Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/bin/java为jdk1.8版本对应的java

安装:

uiAutomatorViewer是安卓调试工具(ADT)中的一部分。所以只需要安装ADT就可以了(不需要安装SDK哦)。

  • 安装Android Studio
  • Android Studio的启动页面当中,选择右下角的Configure -> SDK Manager 会弹出下图:选择Android SDK -> SDK Tools ,按图中勾选,点击OK进入安装,安装完成即可关闭Android Studio。 记住图中Android SDK Location对应
  • Android 手动运行 uiautomator_ui_03


使用

  1. 找到ADT安装目录,在其tools/bin目录下,找到uiAutomatorViewer程序,双击打开。
    例如:ADT的目录为:/Users/liyuan/Library/Android/sdk/tools/bin
  2. Android 手动运行 uiautomator_ui_04


  3. 定位元素

5.3 第三种:appium的appium inspector

appium是目前最流行的开源的app功能测试框架。
同时支持IOSAndroid的自动化测试,也支持多语言(python/java/C#…)

安装

只要安装appium,那么appium inspector也就有了。
appium desktop下载地址:http://appium.io/

使用

  1. 启动appium desktop之后,点击右上角的第一个按钮
  2. 在打开如下界面当中,按图中所示,填写信息
  3. 会根据启动参数,去打开对应的app,然后会截取app当中的页面

总结

以上3种定位告诉你: 任何一个app自动化测试的框架,都会给你提供一个对应的定位工具哦!
android自带的UiAutomator测试框架,提供 uiAutomatorViewer
appium测试框架,提供appium inspector.
python实现的uiAutomator2测试框架,提供weditor 在app自动化测试的过程中,可以使用任意一种或多种来帮你进行元素定位。 推荐使用第一种

6、元素六大定位方式

Android 手动运行 uiautomator_自动化_05

7、元素常用六大操作

7.1 模拟滑屏上拉下拉操作 TouchAction swipe

元素行为

tap屏幕点击 press 按住屏幕 long_press 长按 release 释放 move_to 从一个点移动到另一个店 wait等待

元素操作 perform()

from appium.webdriver.common.touch_action import TouchAction
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time


# 1.准备参数:告诉appium,你要打开哪个设备上的哪个app。
desired_caps = {
    "automationName":"UiAutomator2",
    "platformName":"Android",
    "platformVersion":'7.1.2',
    "deviceName":"emulator-5554",
    "appPackage":"com.dewu.com",
    "appActivity":"com.dewu.activity.WelcomeActivity",
    "noReset":True
}

# 2.、连接appium server,把启动参数发送
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)
# 3.元素定位 使用ID定位方式
loc = (MobileBy.ID,'com/navigation_tiku')
# 4.等待 - 3大等待方式
WebDriverWait(driver,20).until(EC.visibility_of_element_located(loc))
# 5.点击进入题库界面
driver.find_element(*loc).click()  
# 等待所有题库都出来了,再滑动
time.sleep(6)


# 1.获取整屏的大小 size返回一个字典{width,height}
size = driver.get_window_size()

# 2.按压屏幕从一个点移动到另一个点
driver.swipe(size["width"]*0.5,size["height"]*0.9,size["width"]*0.5,size["height"]*0.3,200)

7.2 页面滑动多次寻找某个页面的某一个元素点击 swipe

from appium.webdriver.common.touch_action import TouchAction
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time


# 1.准备参数:告诉appium,你要打开哪个设备上的哪个app。
desired_caps = {
    "automationName":"UiAutomator2",
    "platformName":"Android",
    "platformVersion":'7.1.2',
    "deviceName":"emulator-5554",
    "appPackage":"com.xxx",
    "appActivity":"com.xx.activity.WelcomeActivity",
    "noReset":True
}

# 2.、连接appium server,把启动参数发送
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)
# 3.元素定位
loc = (MobileBy.ID,'com.xx/navigation_tiku')
# 4.等待 - 3大等待方式
WebDriverWait(driver,20).until(EC.visibility_of_element_located(loc))
# 5.点击 进入题库界面
driver.find_element(*loc).click() 
# 6.等待所有题库都出来了,再滑动
time.sleep(3)


# 1、获取整屏的大小
size = driver.get_window_size()

# 循环/滑动
# 什么时候滑?滑几次?滑到什么时候终止?滑到底部了如何不再滑动?
# 滑动以前的内容  != 滑动以后的内容 => 继续滑
# 滑动以前的内容  == 滑动以后的内容 => 已经到底部
"""
# 滑动以前的内容
old = None
# 滑动后的内容
new = driver.page_source
while 滑动以前的内容  != 滑动以后的内容:
    如果元素找着了(driver.find_element()/page_source.find()):
        break
    如果没找着:
        滑动操作 - 
        sleep(3)
        old = new
        new = driver.page_source   
"""
# 滑动以前的内容
old = None
# 返回新页面源码
new = driver.page_source

while old != new:
    try:
        # 通过android uiatumator的UiSelector类查询名为安全测试的元素
        driver.find_element_by_android_uiautomator('new UiSelector().text("安全测试")')
    except:
        # 在200ms内从中心屏幕高度90%的地方滑到屏幕高度30%的地方

        driver.swipe(size["width"]*0.5,size["height"]*0.9,size["width"]*0.5,size["height"]*0.3,200)

        time.sleep(3)

        old = new
        # 重新获取页面源码
        new = driver.page_source
    else:
        print("找到了安全测试")
        break

7.3 多点触控 multi_action

实现了多个单点触控,同时执行**

  • add 添加单点触控的行为
  • 2.perfrom 执行

百度地图放大

from appium.webdriver.common.multi_action import MultiAction


from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
from appium.webdriver.common.touch_action import TouchAction



# 1.准备参数:告诉appium,你要打开哪个设备上的哪个app。
desired_caps = {
    "automationName":"UiAutomator2",
    "platformName":"Android",
    "platformVersion":'7.1.2',
    "deviceName":"emulator-5554",
    "appPackage": "com.baidu.BaiduMap",
    "appActivity": "com.baidu.baidumaps.WelcomeScreen",
    "noReset":True
}

# 2.、连接appium server,把启动参数发送
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)
time.sleep(15)

# 3.、获取整屏的大小
size = driver.get_window_size()

# 4.实例化 TouchAction对象
a = TouchAction(driver)

# 5.一个点从屏幕中心位置移动到屏幕右下角

a.press(x=size["width"]*0.5,y=size["height"]*0.5).wait(200).move_to(x=size["width"]*0.1,y=size["height"]*0.9).release()

# 6.一个点从屏幕中心向右上角滑动

b = TouchAction(driver)
b.press(x=size["width"]*0.5,y=size["height"]*0.5).wait(200).move_to(x=size["width"]*0.9,y=size["height"]*0.1).release()

# 7.加入Multiaction

ma = MultiAction(driver)
ma.add(a,b)
ma.perform()

7.4 tap 最多用5根手指点击一个地方

用法:传一个嵌套元祖的列表。为点的坐标
driver.tap([(100, 20), (100, 60), (100, 100)], 500)

from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
from appium.webdriver.common.touch_action import TouchAction


# 1\准备参数:告诉appium,你要打开哪个设备上的哪个app。
desired_caps = {
    "automationName":"UiAutomator2",
    "platformName":"Android",
    "platformVersion":'7.1.2',
    "deviceName":"emulator-5554",
    "appPackage": "com.baidu.BaiduMap",
    "appActivity": "com.baidu.baidumaps.WelcomeScreen",
    "noReset":True
}

# 3、连接appium server,把启动参数发送
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)


driver.tap()

7.5 toast处理

  1. toast只能用ui automator2来处理
  2. appium server1.15以上版本自动为uiautomtor2, 低于这个版本需要指明"automationName":“UiAutomator2”
  3. toast只有文本,只支持xpath定位, //*[contains(@text,“手机号码”) 不能够用等待元素可见,只能用元素存在否则会报错
from appium import webdriver

from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time


# 1\准备参数:告诉appium,你要打开哪个设备上的哪个app。
desired_caps = {
    "automationName":"UiAutomator2",
    "platformName":"Android",
    "platformVersion":'7.1.2',
    "deviceName":"emulator-5554",
    "appPackage":"com.xx",
    "appActivity":"com.xx.activity.WelcomeActivity",
    "noReset":True
}

# 3、连接appium server,把启动参数发送
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)

loc = (MobileBy.ID,"com.xx:id/navigation_my")
WebDriverWait(driver,20).until(EC.visibility_of_element_located(loc))
driver.find_element(*loc).click()

loc = (MobileBy.ID,"com.xx:id/fragment_my_lemon_avatar_layout")
WebDriverWait(driver,20).until(EC.visibility_of_element_located(loc))
driver.find_element(*loc).click()

loc = (MobileBy.ID,"com.xx:id/btn_login")
WebDriverWait(driver,20).until(EC.visibility_of_element_located(loc))
driver.find_element(*loc).click()

# xpath表达式
loc = (MobileBy.XPATH,'//*[contains(@text,"手机号码或密码")]')
try:
    # 等待元素存在
    # 短期内就会消失的,要缩小巡查周期
    WebDriverWait(driver,6,0.01).until(EC.presence_of_element_located(loc))
    # 获取文本
    text = driver.find_element(*loc).text
    print(text)
except:
    print("我没有获取到toast的提示信息!!")

7.6 应用切换 driver.start_activity(包名,应用名)

from appium import webdriver

from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time


# 1\准备参数:告诉appium,你要打开哪个设备上的哪个app。
desired_caps = {
    "automationName":"UiAutomator2",
    "platformName":"Android",
    "platformVersion":'7.1.2',
    "deviceName":"emulator-5554",
    "appPackage":"com.xx",
    "appActivity":"com.xx.activity.WelcomeActivity",
    "noReset":True
}

# 3、连接appium server,把启动参数发送
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)


loc = (MobileBy.ID,"com.xx/navigation_my")
WebDriverWait(driver,20).until(EC.visibility_of_element_located(loc))
driver.find_element(*loc).click()

# 传包名和应用名
driver.start_activity("com.baidu.BaiduMap","com.baidu.baidumaps.WelcomeScreen")