【笔记】移动端自动化:adb调试工具+appium+UIAutomatorViewer

news2024/11/24 14:22:16

学习源:

https://www.bilibili.com/video/BV11p4y197HQ

https://blog.csdn.net/weixin_47498728/category_11818905.html

一、移动端测试环境搭建

学习目标
1.能够搭建java 环境
2.能够搭建android 环境

(一)整体思路

我们的目标是Android测试,所以环境需要搭建三个,Java,AndroidSDK,Android模拟器。
为什么要安装这三个环境,我们倒着来说:
Android模拟器:实际上就是一台手机,方便我们给大家展示效果。
AndroidSDK: Android SDK给你提供开发测试所必须的Android API类库
Java:Android的底层是c、C++,应用层用的语言是Java所以需要使用Java环境

(二)环境搭建

java环境

安装JDK1.8
运行jdk-8u151-windows-x64.exe文件默认安装即可(例如我的安装目录:C:\Program Files Java\jdk1.8.0)
配置java环境变量(Windowns7为例)
1.进入我的电脑 ->属性 -> 高级系统设置 -> 环境变量
2.在系统变量下点击新建 ->变量名: JAVALHOME -> 变量值: 比如: c:\Program Files Java jdk1.8.-> 点击确定按钮
3.在系统变量下点击新建 -> 变量名: CLASSPATH -> 变量值:;%JAVA_HOME%1ib;%JAVALHOME%1ib\tools.jar(***变量值最前面有一个”")-> 点击确定按钮
4.在系统变量下找到系统的path变量,进入在最后添加:;%JAVAHOME%bin;%JAVALHOME%jre bin(最前面是一个分号,如果path变量最后已有分号,可不用添加) -> 点击确定按钮
验证环境变量
选择语言
1.win+r 或者开始->搜索框输入cmd
2.在界面运行java -version

Android环境

以windows为例:

将SDK保存到硬盘
Android SDK文件央解压到任意目录(记住这个目录的位置,目录不要有中文)
配置环境变量
选择语言
1.进入我的电脑->属性 -> 高级系统设置 -> 环境变量
2.在系统变量下点击新建 →变量名:ANDROID_HOME → 变量值:D:android-sdk -> 点击确定按钮
3.在系统变量下找到系统的path变量,最后添加: ;%ANDROID-HOME%platform-tools;%ANDROID-HOME%too1s;(最前面是一个分号,如果path变量最后已有分号,可不用添加》 -> 点击确定按钮
验证环境变量
重启命令行工具,命令行输入adb,不报错即可

Andriod模拟器

以模拟器Genymotion为例:
1.执行genymotion-2.11.0-vbox.exe(是一个集合程序,包含genymotion和virtualbox) → 不需要更改配置,直接下一步默认安装
2.安装完enymotion继续等待,会提示安装virtualbox,继续安装,期间会提示安装oracle插件,全部允许安装
3.安装完成后会在桌面展示genymotion和virtualbox两个图标

(三)为虚拟机提供安装apk功能

1.打开virtualbox
2.进入virtualbox -> 管理 -> 导入虚拟电脑
3.点击文件选择(Samsung Galaxy s6 - 5.1.0 API 22 - 1440x2560va) -> 点击下一步
4.勾选 重新初始化所有网卡的MAC地址
5.点击导入按钮 ->等待倒入完成
6.virtualbox列表会展示如下图圈出的选项

二、adb 调试工具

学习目标:
1.能够了解 adb 的工作原理

2.能够应用常用的 adb 命令

(一)adb 的工作原理

1.adb的概念
ADB 全名Android Debug Bridge,是一个调试工具

  • 开发安卓应用的程序员必须要掌握
  • 测试工程师在做安卓应用测试时,会使用到
  1. adb的构成和工作原理
    提示:只需要知道组成部分和工作原理,会使用即可,面试可能会被问到
    adb 包含三个部分:

1.Client端: 运行在开发机器中,即你的开发电脑,用来发送 adb 命令;

2.Daemon 守护进程: 运行在调试设备中,手机或模器,用来接收并执行 adb 命令;

3.Server端: 同样运行在开发机器中,用来管理 Client 端和手机的 Daemon 之间的通信。

在这里插入图片描述

小结
adb 工具可以在电脑通过终端命令操作安卓手机/模拟器。

  • adb构成

client端,在电脑上,负责发送adb命令

daemon守护进程,在手机上,负责接收和执行adb命令

server端,在电脑上,负责管理client和daemon

  • adb构成和工作原理

client端,在电脑上,负责发送adb命令
daemon守护进程,在手机上,负责接收和执行adb命令
server端,在电脑上,负责管理client和daemon之间的通信

adb工作原理
1.client端将命令发送给server端
2.server端会将命令发送给daemon端
3.daemon端进行执行
4.将执行结果,返回给server端
5.server端将结果再返回给client端

二)adb 常用命令

1.包名和界面名

概念:
包名 (package):决定程序的唯一性 (不是应用的名字)
界面名 (activity) :目前可以理解,一个界面名,对应着一个界面。

2.应用场景
自动化测试需要通过代码的形式告诉手机测试哪个应用程序的哪一个界面,所以需要通过这个命令进行获取。

使用步骤
1.打开需要测试的应用程序
2.输入 adb 命令

查询命令:

Mac/Linux:

adb shell dumpsys window windows | grep mFocused

Windows:

adb shell dumpsys window windows | findstr mFocusedApp

示例
作用:获取设置程序的包名和界面名
1.先在模拟器或手机中打开《设置》应用程序
2.输入对应平台的命令
结果如下:

mFocusedApp=AppwindowToken{53309da token=Token{2e2fa785ActivityRecord{2928d4fc u0 com.android.settings/.Settings t1127}}}

其中:

  • 包名为: com.android.settings
  • 界面名为:.settings
    注意点
    界面名可能会在和同事沟通交流或网站的文章中翻译为启动名

2.文件传输[应用]

(1)发送文件到手机
(2)从手机中拉取文件

(1)发送文件到手机

将手机需要的数据(数据库文件) 在电脑上调整好,直接发送给手机

命令格式

adb push 电脑的文件路径 手机的文件夹路径

示例
作用:将桌面的 a.txt 发送到手机的 sd 卡

adb push c:\users hm\Desktop\a.txt /sdcard

(2)从手机中拉取文件

将手机产生的文件(数据库文件,日志文件) 拉取到电脑中

应用场景
将手机产生的文件 (数据库文件,日志文件) 拉取到电脑中命令格式

adb pull 手机的文件路径 电脑的文件夹路径

示例
作用:将手机的 sd 卡的 a.txt 拉取到桌面

adb pull /sdcard/a.txt c: users\hm\Desktop

3. 获取app启动时间[应用]

应用场景
如果企业对应用程序的启动速度有要求,则需要使用这个命令进行测试2.测试标准:参照同类软件,启动时间不能超出一倍即可

命令格式

adb she11 am start -w 包名/启动名

示例
作用:启动 com.android.settings 程序并且进入主界面(.Settings )

adb shell am start -w com.android.settings/.Settings

在这里插入图片描述

解释:
1.ThisTime :该界面( activity )启动耗时 (毫秒)
2.TotalTime:应用自身启动耗时= ThisTime +应用application 等资源启动时间 (毫秒)

3.waitTime:系统启动应用耗时 = TotalTime +系统资源启动时间 (毫秒)

在这里插入图片描述

4.获取手机日志[应用]

应用场景
将bug的日志信息发送给开发人员,便于开发人员定位bug
使用步骤
1.打开需要测试的应用程序
2.找到触发bug的位置
3.使用查看日志命令adb 1ogcat
4.触发bug
5.获取日志信息

命令格式

adb logcat

注意:实时监听操作

其它命令

其它命令
1、安装 app 到⼿机:adb install 路 径/xx.apk
2、卸载⼿机上的 app:adb uninstall 包名
3、 获取当前电脑已经连接设备和对应的设备号 :adb devices
4、 进⼊到安卓⼿机内部的linux 系统命令⾏中 :adb shell
5 、启动 adb 服务端:adb start-server
6 、停⽌ adb 服务端:adb kill-server
7、查看 adb 帮助: adb --help

三、Appium自动化测试框架

1.能够安装Appium 桌面客户端
2.能够安装Appium-python 库

Appium介绍
Appium是一个移动端的自动化框架,可用于测试原生应用,移动网页应用和混合型应用,且是跨平台的,可用于iOs和Android操作系统。原生的应用是指用andrid或iOS的sdk编写的应用,移动网页应用是指网页应用,类似于ios中safari应用或者Chrome应用或者类浏览器的应用。混合应用是指一种包裹webview的应用,原生应用于网页内容交互性的应用。
重要的是Appium是跨平台的,何为跨平台,意思就是可以针对不同的平台用一套api来编写测试脚本,

Appium自动化测试环境搭建
我们使用Appium和python来进行自动化测试,需要安装两个东西,一个是Appium的客户端,一个是Appium.python库。这两个需要安装的东西在加上手机就可以进行自动化测试。

它们之间的关系是: python代码·>Appium-python库->Appium -> 手机。

Appium桌面客户端安装方式
1.运行appium-desktop-Setup-1.6.2.exe,默认安装即可

2.启动客户端,按图片步骤 1-> 2->3-> 4 设置

在这里插入图片描述

3.启动成功展示如下图
在这里插入图片描述

Appium-python库安装

命令行安装(需要联网)

pip3 insta17 Appium-Python-Client

可能出现的问题:
错误提示: could not create /usr/local/lib/python2.7/dist-packages/virtualenv_support’: Permission denied
权限问题,使用管理员运行cmd

hello appium 初体验

  1. 能够使用 appium 启动任意应用程序

  2. 能够了解 “前置代码” 中各项参数的含义

01_hello-appium_快速体验_哔哩哔哩_bilibili

应用场景

在做app自动化的时候,我们肯定是针对某个产品、某个软件进行测试,那么我们一定是先让模拟器或
真机帮我们打开这款软件才可以。所以接下来要学的就是如何打开某个应用程序。

需求

使用以下步骤可以打开模拟器中的 《设置》 应用程序

步骤

  1. 打开手机模拟器

  2. 打开appium工具

  3. 创建一个python项目,取名为 hello_appium

  4. 创建一个 demo.py 文件

  5. 将下面代码直接复制,并运行即可

# 导模块
from appium import webdriver
# 创建一个字典,包装相应的启动参数
desired_caps = dict()
# 需要连接的手机的平台(不限制大小写)
desired_caps['platformName'] = 'Android'
# 需要连接的手机的版本号(比如 5.2.1 的版本可以填写 5.2.1 或 5.2 或 5 ,以此类推)
desired_caps['platformVersion'] = '5.1'
# 需要连接的手机的设备号(andoird平台下,可以随便写,但是不能不写)
desired_caps['deviceName'] = '192.168.56.101:5555'
# 需要启动的程序的包名
desired_caps['appPackage'] = 'com.android.settings'
# 需要启动的程序的界面名
desired_caps['appActivity'] = '.Settings'
# 连接appium服务器
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
# 退出
driver.quit()

启动过程
appium的启动实际上是在本机使用了4723端口开启了一个服务

我们写的 python 代码会访问本机的 appium 服务器,并获取 driver 对象
appium 会将我们的 driver 对象调用的方法转化成 post 请求,提交给appium服务器
appium 通过接收到的 post 请求发送给手机,再由手机进行执行

如何使用appium打开任意一个应用程序
步骤
1.打开要测试的应用
2.使用 adb 命令获取包名和界面名
3.修改 desired_caps 字典中的appPackage和appActivity 的参数
如果测试的设备版本号发生变化?
修改 desired_caps 字典中的 platformVersion 的参数
如果测试的设备平台发生变化?
修改desired_caps字典中的 platformName的参数

Appium 基础操作 API

  1. 能够使用 appium 在脚本内启动其他 app

  2. 能够使用 appium 获取包名和界面名

  3. 能够使用 appium 关闭 app 和 驱动对象

  4. 能够使用 appium 安装和卸载 app

  5. 能够使用 appium 将应用置于后台

一. Appium 基础操作 API

前置代码

from appium import webdriver
desired_caps = dict()
# 手机参数
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# 应用参数
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
# 退出driver
driver.quit()

1.在脚本内启动其他 app

应用场景
如果一个应用需要跳转到另外一个应用,就可以使用这个 api 进行应用的跳转,就像我们通过外卖应用下订单之后会跳转到支付应用一样。

方法名和参数

# 脚本内启动其他app
# 参数:
# appPackage:要打开的程序的包名
# appActivity:要打开的程序的界面名
driver.start_activity(appPackage, appActivity)

示例
打开《设置》应用程序,等待三秒后跳转到《短信》应用程序

from appium import webdriver
desired_caps = dict()
# 手机参数
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# 应用参数
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
# 跳转到短信
driver.start_activity('com.android.mms', '.ui.ConversationList')
time.sleep(5)
# 退出driver
driver.quit()

2.获取 app 的包名和界面名

应用场景

当我们从一个应用跳转到另外一个应用的时候,想输出其包名、界面名或者想在报告中展现对应信息,我们就可以调用这个属性来进行获取。

属性名

# 获取包名
driver.current_package
# 获取界面名
driver.current_activity

示例

打开《设置》应用程序后输出当前的包名和界面名

from appium import webdriver
desired_caps = dict()
# 手机参数
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# 应用参数
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
# 打印当前包名
print(driver.current_package)
# 打印当前界面名
print(driver.current_activity)
# 退出driver
driver.quit()

结果

com.android.settings
.Settings

3.关闭 app 和 驱动对象

应用场景
有的时候我们需要关闭某个应用程序后,再打开新的应用。那么如何关闭应用程序呢?

方法名

# 关闭当前操作的app,不会关闭驱动对象
driver.close_app()
# 关闭驱动对象,同时关闭所有关联的app
driver.quit()

示例
打开《设置》,使用 close_app() 方法关闭,再尝试使用 quit() 方法,最后打印当前程序的包名,观察区别

from appium import webdriver
desired_caps = dict()
# 手机参数
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# 应用参数
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
# 关闭应用
driver.close_app()
# 退出driver
driver.quit()

结果
使用 quit() 后会报错,使用close_app() 不会报错
小结
close_app() 不会关闭驱动对象,只会关闭应用
quit() 会关闭驱动对象

4.安装和卸载以及是否安装 app

应用场景
一些应用市场的软件可能会有一个按钮,如果某一个程序已经安装则卸载,如果没有安装则安装

方法名

# 安装app
# 参数:
# app_path:apk路径
driver.install_app(app_path)
# 卸载app
# 参数:
# app_id:应用程序包名
driver.remove_app(app_id)
# 判断app是否已经安装
# 参数:
# app_id:应用程序包名
# 返回值:
# 布尔类型,True为安装,False为没有安装
driver.is_app_installed(app_id)

示例
如果《安智市场》已经安装,则卸载《安智市场》,如果没有则安装

from appium import webdriver
desired_caps = dict()
# 手机参数
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# 应用参数
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
if driver.is_app_installed("cn.goapk.market"):
    driver.remove_app("cn.goapk.market")
else:
    driver.install_app("/Users/Yoson/Desktop/anzhishichang.apk")
# 退出driver
driver.quit()

5.将应用置于后台

应用场景
银行类 app 会在进入后台一定时间后,如果再回到前台页面会重新输入密码,如果需要自动化测试这种功能:

# app放置到后台一定时间后再回到前台,模拟热启动
# 参数:
# seconds:后台停留多少秒
driver.background_app(seconds)

示例
打开《设置》应用,进入后台 5 秒,再回到前台

from appium import webdriver
desired_caps = dict()
# 手机参数
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# 应用参数
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
# 获取driver
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
time.sleep(3)
print("---- 准备进入后台 ----")
# 进入后台5秒,再回到前台
driver.background_app(5)
print("---- 以及回到前台 ----")
time.sleep(3)
# 退出driver
driver.quit()

注意点
热启动:表示进入后台回到前台
冷启动:关机再开这种切断电源的行为

四、UIAutomatorViewer

UIAutomatorViewer初体验

学习目标:
能够使用 UIAutomatorViewer 获取元素的特征信息

应用场景
定位元素的时候必须根据元素的相关特征来进行定位,而 UIAutomatorViewer 就是用来获取元素特征的。

简介
UIAutomatorViewer 用来扫描和分析 Android 应用程序的 UI 控件的工具。

使用步骤
1.进入SDK目录下的目录
mac 在 tools/bin 目录下,打开 uiautomatorviewer
windows 在 tools 目录下,打开 uiautomatorviewer.bat
2.电脑连接真机或打开android模拟器
3.启动待测试app
4.点击 uiautomatorviewer 的左上角 Device Screenshot (从左数第二个按钮)
5.点击希望查看的控件
6.查看右下角 Node Detail 相关信息

示例
查看《设置》应用程序右上角 ”放大镜“ 按钮的 ”resource-id“

1.打开 uiautomatorviewer
2.打开 android 模拟器
3.启动《设置》应用程序
4.点击 Device Screenshot 按钮
5.点击 ”放大镜“ 按钮
6.查看 Node Detail 中的 ”resource-id“ 信息
在这里插入图片描述

注意点
1.自动打开的命令行窗口不要关
如果关了,整个工具也会关闭

2.打开uiautomatorviewer闪退
解决方案:
jdk版本问题造成的,jdk为1.9时可能会出现这个问题,请换成1.8的版本

3.点击第二个按钮报错

在这里插入图片描述

解决方案:重启adb

  • adb kill-server

  • adb start-server

元素定位操作 API

学习目标

  1. 能够分别使用 id、class、xpath 定位某一个元素

  2. 能够分别使用 id、class、xpath 定位某一组元素

应用场景
计算机不像人一样 ”聪明“,我们需要通过元素定位来获取元素,才能让计算机帮我们 ”操作“ 这个元素。

步骤

打开 uiautomatorviewer 工具
打开模拟器或真机
通过 uiautomatorviewer 工具获取想要进行操作的元素的 Node Detail 信息
通过元素定位 API 进行定位
对元素进行相关操作
注意点
元素的定位基于当前屏幕范围内展示的可见元素。

1.定位一个元素【掌握】

应用场景
想要对按钮进行点击,想要对输入框进行输入,想要获取文本框的内容,定位元素是自动化操作必须要使用的方法。只有获取元素之后,才能对这个元素进行操作。

方法名

# 通过id定位一个元素
# 参数:
# id_value:元素的resource-id属性值
# 返回值:
# 定位到的单个元素
driver.find_element_by_id(id_value)
# 通过class_name定位一个元素
# 参数:
# class_value:元素的class属性值
# 返回值:
# 定位到的单个元素
driver.find_element_by_class_name(class_value)
# 通过xpath定位一个元素
# 参数:
# xpath_value:定位元素的xpath表达式
# 返回值:
# 定位到的单个元素
driver.find_element_by_xpath(xpath_value)

示例
通过 id 的形式,定位 ”放大镜“ 按钮,并点击
通过 class 的形式,定位 ”输入框“,输入 ”hello“
通过 xpath 的形式,定位"收起"按钮,并点击

关键代码

# 通过 id 的形式,定位 ”放大镜“ 按钮,并点击
driver.find_element_by_id("com.android.settings:id/search").click()

# 通过 class 的形式,定位 ”输入框“,输入 ”hello“
driver.find_element_by_class_name("android.widget.EditText").send_keys("hello")

# 通过 xpath 的形式,定位"收起"按钮,并点击
driver.find_element_by_xpath("//*[@content-desc='收起']").click()

小结:

  • find_element_by_id
    传入的参数: resource-id 的值

  • find_element_by_class_name
    传入的参数: class 的值

  • find_element_by_xpath
    传入的参数:xpath 表达式。

注意点:

如果很多元素特征相同,使用 find_element_by_xxx 的方法会找到第一个

也就是说,尽量去找元素特征有唯一性的“特征”来定位

2.定位一组元素【掌握】

应用场景
和定位一个元素相同,但如果想要批量的获取某个相同特征的元素,使用定位一组元素的方式更加方便。

方法名

# 通过id定位一组元素
# 参数:
# id_value:元素的resource-id属性值
# 返回值:
# 列表,定位到的所有符合调价你的元素
driver.find_elements_by_id(id_value)
# 通过class_name定位一组元素
# 参数:
# class_value:元素的class属性值
# 返回值:
# 列表,定位到的所有符合调价你的元素
driver.find_elements_by_class_name(class_value)
# 通过xpath定位一组元素
# 参数:
# xpath_value:定位元素的xpath表达式
# 返回值:
# 列表,定位到的所有符合调价你的元素
driver.find_elements_by_xpath(xpath_value)

示例
通过 id 的形式,获取所有 resource-id 为 ”com.android.settings:id/title“ 的元素,并打印其文字内容
通过 class_name 的形式,获取所有class 为 ”android.widget.TextView“ 的元素,并打印其文字内容
通过 xpath 的形式,获取所有包含 ”设“ 的元素,并打印其文字内容

关键代码

# 通过 id 的形式,获取所有 resource-id 为 ”com.android.settings:id/title“ 的元素,并打印其文字内容
titles = driver.find_elements_by_id("com.android.settings:id/title")
for title in titles:
	print(title.text)
# 通过 class_name 的形式,获取所有class 为 ”android.widget.TextView“ 的元素,并打印其文字内容
text_views = driver.find_element_by_class_name("android.widget.TextView")
for text_view in text_views:
	print(text_view.text)
# 通过 xpath 的形式,获取所有包含 ”设“ 的元素,并打印其文字内容
elements = driver.find_element_by_xpath("//*[contains(@text,'设')]")
for element in elements:
	print(element.text)

小结:

  • find_elements_by_id
    传入的参数: resource-id 的值

  • find_elements_by_class_name
    传入的参数: class 的值

  • find_elements_by_xpath
    传入的参数:xpath 表达式。

概念:
如果通过一组的方式进行定位,获取的返回值不再是一个元素。而是一个列表,列表中装着所有符合这个特征的元素。

3.定位元素的注意点【了解】

应用场景
了解这些注意点可以以后在出错误的时候,更快速的定位问题原因。

示例
使用 find_element_by_xx 或 find_elements_by_xx 的方法,分别传入一个没有的 ”特征“ 会是什么结果呢?
核心代码
driver.find_element_by_id(“xxx”)

在这里插入图片描述

driver.find_elements_by_id(“xxx”)

在这里插入图片描述

小结
1.如果使用 find_element_by_xx 方法,如果传入一个没有的特征,会报NoSuchElementException
的错误。
2.如果使用 find_elements_by_xx 方法,如果传入一个没有的特征,不会报错,会返回一个空列表

元素等待

学习目标
能够使用隐式等待来定位元素
能够使用显式等待来定位元素

应用场景
可能由于一些原因,我们想找的元素并没有立刻出来,此时如果直接定位可能会报错,比如以下原因:

1.由于网络速度原因
2.服务器处理请求原因
3.电脑配置原因
概念
WebDriver定位页面元素时如果未找到,会在指定时间内一直等待的过程
元素等待一共分为两种类型

1.显式等待
2.隐式等待

1.隐式等待

示例
在 5 秒钟内,在《设置》程序中的 ”返回“ 按钮,如果找到则点击。如果找不到则观察对应错误信息。

核心代码

from selenium.webdriver.support.wait import WebDriverWait
# -----
# 启动应用参数
# -----
driver.implicitly_wait(5)
search_button = driver.find_element_by_xpath("//*[contains(@content-desc,'收起')]")
search_button.click()

方法参数解释

# 参数:
# timeout:超时的时长,单位:秒
implicitly_wait(timeout)

关键方法:

通过 driver 对象调用implicitly_wait 方法
设置超时时间

作用:

在设置了超时时间之后,后续所有的定位元素的方法都会在这个时间内等待元素的出现。
如果出现了,直接进行后续操作。
如果没有出现,报错,NoSuchElementException。

2.显示等待

应用场景
针对所有定位元素的超时时间设置为不同的值的时候

概念
等待元素加载指定的时长,超出时长抛出TimeoutException异常

步骤

1.导包
2.创建 WebDriverWait 对象
3.调用 WebDriverWait 对象的 until 方法
示例
在 5 秒钟内,每 1 秒在《设置》程序中的 ”返回“ 按钮,如果找到则点击。如果找不到则观察对应错误信息。

核心代码

from selenium.webdriver.support.wait import WebDriverWait
# -----
# 启动应用参数
# -----
# 创建WebDriverWait对象
wait = WebDriverWait(driver, 5, poll_frequency=1)
# 获取元素并设置超时时间和频率
search_button = wait.until(lambda x: x.find_element_by_xpath("//*[contains(@content-desc,'收起')]"))
# 点击搜索按钮,没有显示等待
search_button.click()

方法参数解释

# 参数:
# driver:驱动对象
# timeout:超时的时长,单位:秒
# poll_frequency:检测间隔时间,默认为0.5秒
# 返回值:
# WebDriverWait对象
WebDriverWait(driver, timeout, poll_frequency=0.5)
# 参数:
# method:lambda查找元素表达式
# 返回值:
# 定位到的元素,如果没有定位到会抛出TimeoutException异常
wait.until(method)

隐式等待和显式等待的选择

作用域:
显式等待为单个元素有效(更灵活),隐式为全局元素(更简单)

方法:
显式等待方法封装在 WebDriverWait 类中,而隐式等待则直接通过 driver 实例化对象调用

关于 sleep 的形式?
sleep 是固定死一个时间,不是不行,是不推荐。
元素等待可以让元素出来的第一时间进行操作。sleep 可能造成不必要的浪费。

元素操作API

学习目标

1.能够使用代码点击按钮
2.能够使用代码对输入框输入文字3.能够使用代码对输入框清空文字
4.能够使用代码获取元素的文本内容
5.能够使用代码获取元素的位置和大小
6.能够使用代码根据属性名获取元素的属性值

1.点击元素click()

应用场景
需要点击某个按钮的时候使用

方法名

# 对element按钮进行点击操作
element.click()

示例

  1. 打开《设置》

  2. 点击放大镜按钮

driver.find_element_by_id("com.android.settings:id/search").click()

2.输入和清空输入框内容 send_keys() 和 clear()

应用场景
需要对输入框进行输入或清空的时候使用

方法名

# 对element输入框进行输入操作
# 参数:
# value:输入的内容
element.send_keys(value)
# 对element输入框进行输入操作
element.clear()

示例

1.打开《设置》
2.点击 ”放大镜“
3.输入 ”hello“
4.暂停 2 秒
5.清空所有文本内容
6.暂停 5 秒
7.输入 ”你好“

默认输入中文无效,但不会报错,需要在 ”前置代码“ 中增加两个参数
desired_caps[‘unicodeKeyboard’] =True
desired_caps[‘resetKeyboard’] = True

核心代码

desired_caps[‘unicodeKeyboard’] =True
desired_caps[‘resetKeyboard’] = True
...
driver.find_element_by_id("com.android.settings:id/search").click()
edit_text = driver.find_element_by_class_name("android.widget.EditText")
edit_text.send_keys("hello")
time.sleep(2)
edit_text.clear()
time.sleep(5)
edit_text.send_keys("你好")

3.获取元素的文本内容

应用场景
需要获取按钮、文本框、输入框等控件的文本内容时使用

属性名

# 获取element控件的文本内容
# 返回值:
# 控件的文本内容
element.text

示例
1.打开《设置》
2.获取所有 resource-id 为 ”com.android.settings:id/title“ 的元素,并打印其文字内容

核心代码

titles = driver.find_elements_by_id("com.android.settings:id/title")
for title in titles:
	print(title.text)

4.获取元素的位置和大小

应用场景
需要获取元素的位置和大小的时候使用

属性名

属性名

# 获取element的位置
# 返回值:
# 字典,x为元素的x坐标,y为元素的y坐标
element.location
# 获取element的大小
# 返回值:
# 字典,width为宽度,height为告诉
element.size
# 获取element的位置
# 返回值:
# 字典,x为元素的x坐标,y为元素的y坐标
element.location
# 获取element的大小
# 返回值:
# 字典,width为宽度,height为告诉
element.size

示例
1.打开《设置》
2.获取 ”放大镜“ 的位置和大小

核心代码

search_button = driver.find_element_by_id("com.android.settings:id/search")
print(search_button.location)
print(search_button.size)

5.获取元素的属性值

应用场景
根据特征定位到元素后,使元素的属性名获取对应的属性值

方法名

# 对element进行点击操作
# 参数:
# value:要获取的属性名
# 返回值:
# 根据属性名得到的属性值
element.get_attribute(value) # value:元素的属性

示例
1.打开《设置》
2.获取所有 resource-id 为 ”com.android.settings:id/title“ 的元素
3.使用 get_attribute 获取这些元素的 enabled、text、content-desc、resource-id、class 的属性值

核心代码

titles = driver.find_elements_by_id("com.android.settings:id/title")
for title in titles:
print(title.get_attribute("enabled"))
print(title.get_attribute("text"))
print(title.get_attribute("name"))
# 特殊:resourceId、ClassName固定属性名
print(title.get_attribute("resourceId"))
print(title.get_attribute("ClassName"))

注意点:
value=‘text’ 返回text的属性值
value=‘name’ 返回content-desc / text属性值
value=‘className’ 返回 class属性值,只有 API=>18(即安卓版本大于4.3) 才能支持
value=‘resourceId’ 返回 resource-id属性值,只有 API=>18 (即安卓版本大于4.3) 才能支持
在这里插入图片描述

滑动和拖拽事件

学习目标
1.能够使用 swipe 滑动屏幕
2.能够使用 scroll 滑动屏幕
3.能够使用 drag_ahd_drop 滑动屏幕

应用场景
我们在做自动化测试的时候,有些按钮是需要滑动几次屏幕后才会出现,此时,我们需要使用代码来模拟手指的滑动,也就是我们将要学习的滑动和拖拽事件

swipe 滑动事件

driver.swipe(起始x坐标起始y坐标,结束x坐标结束y坐标持续时间ms)

概念
从一个坐标位置滑动到另一个坐标位置,只能是两个点之间的滑动。

方法名

# 从一个坐标位置滑动到另一个坐标位置,只能是两个点之间的滑动
# 参数:
# start_x: 起点X轴坐标
# start_y: 起点Y轴坐标
# end_x: 终点X轴坐标
# end_y: 终点Y轴坐标
# duration: 滑动这个操作一共持续的时间长度,单位:ms
driver.swipe(start_x, start_y, end_x, end_y, duration=None)

示例1
模拟手指从(100, 2000),滑动到(100, 1000)的位置

核心代码

driver.swipe(100, 2000, 100, 1000)

示例2
模拟手指从(100, 2000),滑动到(100, 100)的位置

核心代码

driver.swipe(100, 2000, 100, 100)

示例3

模拟手指从(100, 2000),滑动到(100, 100)的位置,持续5秒

核心代码

driver.swipe(100, 2000, 100, 100, 5000)

小结
距离相同时,持续时间越长,惯性越小
持续时间相同时,手指滑动的距离越大,实际滑动的距离也就越大

为什么每次运行滑动的时候会有一些误差

cpu工作量大会导致计时器的时间和真实时间有误差

在这里插入图片描述

scroll 滑动事件

driver.scroll(起始元素, 结束元素)

概念
从一个元素滑动到另一个元素,直到页面自动停止。

方法名

# 从一个元素滑动到另一个元素,直到页面自动停止
# 参数:
# origin_el: 滑动开始的元素
# destination_el: 滑动结束的元素
driver.scroll(origin_el, destination_el)

示例
从 “存储” 滑动到 “更多”

核心代码

save_button = driver.find_element_by_xpath("//*[@text='存储']")
more_button = driver.find_element_by_xpath("//*[@text='更多']")
driver.scroll(save_button, more_button)

小结
不能设置持续时间,惯性很大

drag_and_drop 拖拽事件

driver.drag_and_drop(起始元素, 结束元素)

概念
从一个元素滑动到另一个元素,第二个元素替代第一个元素原本屏幕上的位置。

方法名

# 从一个元素滑动到另一个元素,第二个元素替代第一个元素原本屏幕上的位置
# 参数:
# origin_el: 滑动开始的元素
# destination_el: 滑动结束的元素
driver.drag_and_drop(origin_el, destination_el)

示例

将 “存储” 拖拽到 “更多”

核心代码

save_button = driver.find_element_by_xpath("//*[@text='存储']")
more_button = driver.find_element_by_xpath("//*[@text='更多']")
driver.drag_and_drop(save_button, more_button)

小结
不能设置持续时间,没有惯性

滑动和拖拽事件的选择

滑动和拖拽无非就是考虑是否有 “惯性” ,以及传递的参数是 “元素” 还是 “坐标”。

可以分成以下四种情况
- 有 “惯性” ,传入 “元素”
scroll
- 无 “惯性” ,传入 “元素”
drag_and_drop
- 有 “惯性” ,传入 “坐标”
swipe,并且设置较短的 duration 时间
- 无 “惯性” ,传入 “坐标”
swipe,并且设置较长的 duration 时间

小结
在这里插入图片描述

高级手势TouchAction

学习目标
1.能够使用代码完成轻敲手势
2.能够使用代码完成按下手势
3.能够使用代码完成抬起手势
4.能够使用代码完成等待操作
5.能够使用代码完成长按手势
6.能够使用代码完成手指移动操作

应用场景
TouchAction 可以实现一些针对手势的操作,比如滑动、长按、拖动等。我们可以将这些基本手势组合成一个相对复杂的手势。比如,我们解锁手机或者一些应用软件都有手势解锁的这种方式。

使用步骤
1.创建 TouchAction 对象
2.通过对象调用想执行的手势
3.通过 perform() 执行动作

注意点
所有手势都要通过执行perform()函数才会运行。

1.轻敲操作【掌握】- tap()

应用场景
模拟手指对某个元素或坐标按下并快速抬起。比如,固定点击(100, 100)的位置。

方法名

# 模拟手指对元素或坐标的轻敲操作
# 参数:
# element:元素
# x:x坐标
# y:y坐标
TouchAction(driver).tap(element=None, x=None, y=None).perform()

示例
打开《设置》
轻敲 “WLAN”

核心代码

el = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")
TouchAction(driver).tap(el).perform()

2.按下和抬起操作【掌握】- press() 和release()

应用场景
模拟手指一直按下,模拟手指抬起。可以用来组合成轻敲或长按的操作

方法名

# 模拟手指对元素或坐标的按下操作
# 参数:
# el:元素
# x:x坐标
# y:y坐标
TouchAction(driver).press(el=None, x=None, y=None).perform()
# 模拟手指对元素或坐标的抬起操作
TouchAction(driver).release().perform()

示例1
使用坐标的形式按下 WLAN (650, 650),2 秒后,按下(650, 650)的位置

核心代码

TouchAction(driver).press(x=650, y=650).perform()
time.sleep(2)
TouchAction(driver).press(x=650, y=650).perform()

示例2
使用坐标的形式按下 WLAN (650, 650),2 秒后,按下(650, 650)的位置,并抬起

TouchAction(driver).press(x=650, y=650).perform()
time.sleep(2)
TouchAction(driver).press(x=650, y=650).release().perform()

3.等待操作【掌握】- wait()

应用场景
模拟手指等待,比如按下后等待 5 秒之后再抬起。

方法名

# 模拟手指暂定操作
# 参数:
# ms:暂停的毫秒数
TouchAction(driver).wait(ms=0).perform()

示例

使用坐标的形式点击 WLAN (650, 650),2 秒后,按下(650, 650)的位置,暂停 2 秒,并抬起

核心代码

TouchAction(driver).tap(x=650, y=650).perform()
time.sleep(2)
TouchAction(driver).press(x=650, y=650).wait(2000).release().perform()

4.长按操作【掌握】-long_press()

应用场景

模拟手指对元素或坐标的长按操作。比如,长按某个按钮弹出菜单。

方法名

# 模拟手指对元素或坐标的长按操作
# 参数:
# el:元素
# x:x坐标
# y:y坐标
# duration:长按时间,毫秒
TouchAction(driver).long_press(el=None, x=None, y=None, duration=1000).perform()

示例
使用坐标的形式点击 WLAN (650, 650),2 秒后,长按(650, 650)的位置持续 2 秒

核心代码

TouchAction(driver).tap(x=400, y=400).perform()
time.sleep(2)
TouchAction(driver).long_press(x=400, y=400, duration=2000).release().perform()

5.移动操作【掌握】- move_to

应用场景
模拟手指移动操作,比如,手势解锁需要先按下,再移动。

方法名

# 模拟手指对元素或坐标的移动操作
# 参数:
# el:元素
# x:x坐标
# y:y坐标
TouchAction(driver).move_to(el=None, x=None, y=None).perform()

示例
在手势解锁中,画一个如下图的案例
包名界面名为 com.android.settings/.ChooseLockPattern

在这里插入图片描述

核心代码

TouchAction(driver).press(x=246, y=857).move_to(x=721, y=867).move_to(x=1200,
y=851).move_to(x=1200, y=1329).move_to(x=724, y=1329).move_to(x=246,
y=1329).move_to(x=718, y=1815).release().perform()

手机操作API

学习目标
能够获取手机分辨率
能够获取手机截图
能够获取和设置网络状态
能够发送键到设备
能够打开和关闭手机通知栏

1.获取手机分辨率【掌握】

应用场景
自动化测试可能会需要根据当前设备的屏幕分辨率来计算一些点击或者滑动的坐标

方法名

# 获取手机分辨率
driver.get_window_size()

示例

输出当前设备的屏幕分辨率

核心代码

print(driver.get_window_size())

执行结果

{‘height’: 800, ‘width’: 480}

2.手机截图【掌握】

应用场景
有些自动化的操作可能没有反应,但并不报错。此时我们就可以将操作过后的关键情况,截图留存。后期也可以根据图片发现问题。

方法名

# 获取手机分辨率
# 参数:
# filename:指定路径下,指定格式的图片
get_screenshot_as_file(filename)

示例
打开设置页面
截图当前页面保存到当前目录,命名为screen.png

核心代码

driver.get_screenshot_as_file(os.getcwd() + os.sep + './screen.png')

3.获取和设置手机网络【掌握】

应用场景
视频应用在使用流量看视频的时候,大部分都会提示用户正在是否继续播放。作为测试人员,我们可能需要用自动化的形式来判断是否有对应的提示。即,用流量的时候应该有提示,不用流量的时候应该没有提示。

获取手机网络

属性名

# 获取手机网络 driver.network_connection

示例

获取当前网络类型,并打印

核心代码

print(driver.network_connection)

执行结果

6

结果对照

在这里插入图片描述

设置手机网络

方法名

# 设置手机网络
# 参数:
# connectionType:网络类型
driver.set_network_connection(connectionType)

示例

设置当前设备为飞行模式

核心代码

driver.set_network_connection(1)

执行效果

设备变为飞行模式

4. 发送键到设备【掌握】

应用场景
模拟按 “返回键” “home键” 等等操作,比如,很多应用有按两次返回键退出应用的功能,如果这个功能需要我们做自动化,那么一定会用到这个方法

方法名

# 发送键到设备
# 参数:
# keycode:发送给设备的关键代码
# metastate:关于被发送的关键代码的元信息,一般为默认值
driver.press_keycode(keycode, metastate=None)

注意点

按键对应的编码,可以在百度搜索关键字 “android keycode”
例如:https://blog.csdn.net/feizhixuan46789/article/details/16801429

示例

点击三次音量加,再点击返回,再点击两次音量减。

核心代码

driver.press_keycode(24)
driver.press_keycode(24)
driver.press_keycode(24)
driver.press_keycode(4)
driver.press_keycode(25)
driver.press_keycode(25)

5.操作手机通知栏【掌握】

应用场景
测试即时通信类软件的时候,如果 A 给 B 发送一条消息,B 的通知栏肯定会显示对应的消息。我们想通过通知栏来判断 B 是否收到消息,一定要先操作手机的通知栏
方法名

#打开手机通知栏
driver.open_notifications()

注意点

appium官方并没有为我们提供关闭通知的api,那么现实生活中怎么关闭,就怎样操作就行,比 如,手指从下往上滑动,或者,按返回键

示例

打开通知栏,两秒后,关闭通知栏

核心代码

driver.open_notifications()
time.sleep(2)
driver.press_keycode(4)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/333732.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小红书情人节大赏!热门话题各出奇招,看看哪个品牌打动了你?

情人节热度狂飙,实时热度值破万 以爱之名,传递爱意。每年情人节向来是不容错过的热门话题。我们发现,临近情人节,小红书平台的相关内容热度飙升。据千瓜数据关键词热度查询,2月初“情人节”热搜词热度值就已破万。 截止…

1.ORB-SLAM2中的多线程调度解析

目录 0.先修知识 1.ORB - SLAM2中的线程 2.ORBSLAM2中的互斥锁示例 0.先修知识 需要了解C中开辟多线程的方式,了解C中不同锁的使用方法 学习C:C进阶(五)多线程编程原理及多线程编程方法https://blog.csdn.net/qq_41694024/artic…

Java 网络编程详解

1、什么是网络编程 在网络通信协议下,不同计算机上运行的程序,可以进行数据传输。 应用场景:     1、即时通信 2、网游对战 3、邮件等等 Java中可以使用java.net包下的技术轻松开发出常见的网络应用程序 2、网络编程三要素 2.1 IP地址 要…

HCNP路由交换学习指南丨学习笔记丨07.BGP

07.BGP1. BGP 的基本概念1.1 BGP 对等体关系类型1.2 IBGP 水平分割原则1. BGP 的基本概念 关于 自治系统(Autonomous System,AS) 的传统定义:由一个单一的机构或组织所管理的一系列 IP 网络及其设备所构成的集合。 自治系统的简单…

jsp羽毛球场馆管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 jsp 羽毛球场馆管理系统 是一套完善的web设计系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发,数据库为Mysql,…

通达信MACD面积背离指标公式,思路来自于缠论背驰

MACD面积背离指标公式的思路来自于缠论的MACD面积背驰,但是背驰的定义有一些限制条件,编写指标不一定能满足,这里就不纠结了,编写的指标称为MACD面积背离。另外编写这个指标公式需要对缠论有一些了解,如果没有相关基础…

数据结构笔记堆

1.堆的定义//堆是一颗完全二叉树,堆一般由优先队列来实现堆分为两种:1.大顶堆中父亲结点的值大于或者等于孩子结点的值,以它为根结点的子树,它是最大值(顶点是最大值,顶点指的是树的根结点或者子树的根结点)2.小顶堆的父亲结点的值…

详解 matplotlib.pyplot ,Python 初学者真能看懂

Matplotlib 是一个 Python 中的 2D 绘图库, pyplot 模块是一个方便使用 Matplotlib 的接口。 下面是 pyplot 模块中的五个重要的知识点: 【创建图形】: pyplot 模块提供了许多简单易用的函数来创建图形,如 plot、scatter、bar、h…

Python语言零基础入门教程(十)

Python 字符串 字符串是 Python 中最常用的数据类型。我们可以使用引号 ( ’ 或 " ) 来创建字符串。 创建字符串很简单,只要为变量分配一个值即可。例如: var1 Hello World! var2 "Python Runoob"Python 访问字符串中的值 Python 不…

TCP连接的状态详解以及故障排查(五)

同时打开 两个应用程序同时执行主动打开的情况是可能的,虽然发生的可能性较低。每一端都发送一个SYN,并传递给对方,且每一端都使用对端所知的端口作为本地端口。例如: 主机a中一应用程序使用7777作为本地端口,并连接到主机b 888…

【Python入门第四天】Python 注释

开始之前,先给大家讲个笑话… 程序员最讨厌的两种人:写代码不写注释的人和让自己写注释的人。 注释可用于解释 Python 代码。 注释可用于提高代码的可读性。 在测试代码时,可以使用注释来阻止执行。 创建注释 注释以 # 开头&am…

低代码开发平台|生产管理-生产加工搭建指南

1、简介1.1、案例简介本文将介绍,如何搭建生产管理-生产加工。1.2、应用场景在主生产计划列表中下达加工后,在加工单列表可操作领料、质检。2、设置方法2.1、表单搭建1)新建表单【产品结构清单(BOM)】,字段…

32单片机矩阵键盘-同列组合键不能识别故障-已解决

一、电路原理 1.1. 矩阵键盘电路 1.2. gd32f103单片机端是iic,中间经过一个pca9535芯片。 1.3 pca9535 的功能请参考相关文档 这里主要用到的是设置输入输出模式,读取输入值,输出高或者输出低等功能。 二、基本要求 2.1 单个按键识别 2.2 组合键识别…

米尔基于ARM嵌入式核心板的电池管理系统(BMS)

BMS全称是Battery Management System,电池管理系统。它是配合监控储能电池状态的设备,主要就是为了智能化管理及维护各个电池单元,防止电池出现过充电和过放电,延长电池的使用寿命,监控电池的状态。 图片摘自网络 电池…

【C++入门】命名空间,输出输入,缺省参数,函数重载

文章目录命名空间C输入与输出缺省参数函数重载命名空间 在C/C中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标 识符的名称进行本地化&#xff0…

电子技术——共源共栅放大器

电子技术——共源共栅放大器 之前我们提到过,提高基础增益单元(共源放大器)的一种方法是提高其 ror_oro​ 的阻值,之后我们学过共栅放大器作为电流缓冲器可以做到这一点,自然地我们就得到了终极解决方案,也…

Fluid-数据缓存亲和性调度原理解析

前言在Fluid中,Dataset资源对象中所定义的远程文件是可被调度的,这意味着你能够像管理你的Pod一样管理远程文件缓存在Kubernetes集群上的存放位置。另外,Fluid同样支持对于应用的数据缓存亲和性调度,这种调度方式将应用(e.g. 数据…

iOS 导航条isTranslucent几个注意点(iOS11及iOS13的变化)

文章主要针对11及13之后的导航变化进行总结,主要是设置透明度时对转场,包括标题,背景透明,图片,颜色等设置的影响。 每一个iOS版本的发布苹果最不稳写的可能就数这个导航条了吧,改了又改。 因此isTranslu…

Prometheus监控Java-JMX

一、什么是 JMX Exporter ? JMX Exporter 利用 Java 的 JMX 机制来读取 JVM 运行时的一些监控数据,然后将其转换为 Prometheus 所认知的 metrics 格式,以便让 Prometheus 对其进行监控采集。 那么,JMX 又是什么呢?它的全称是&a…

【Redis场景4】单机环境下秒杀问题

单机环境下的秒杀问题 秒杀下单功能及并发测试 完整代码GitHub:https://github.com/xbhog/hm-dianping/tree/20230130-xbhog-redisSpike 秒杀条件分析: 秒杀是否开始或结束,如果尚未开始或已经结束则无法下单库存是否充足,不足…