读后笔记 -- Python 全栈测试开发 Chapter7:移动自动化测试框架
7.1 主流框架优缺点剖析
1. APP 主要测试策略
- 安装、卸载测试:
- 安装:1)安装路径;2)安装环境(平台、安全软件);3)安全权限(获取位置、摄像头、通讯录、ROOT管理员等权限);4)安装的版本;5)严酷测试:a)安装过程中取消;b)安装过程中重启、关机;c)内存不足下安装;d)无网或弱网下进行安装验证;
- 卸载:1)安装路径下删除对应的文件;2)写入系统部分的信息进行删除(PC注册表);3)卸载过程中取消、重启;4)卸载释放空间(软件所产生的数据信息、缓存、应用程序空间)
- UI测试:
- (如菜单、对话框、窗口和其它可规控件)布局、风格是否满足客户要求、文字是否正确、页面是否美观、文字、图片组合是否完美、操作是否友好等
- 主要包含三部分内容:导航、图形、内容
- 功能测试:
- 1)运行APP(安装完成后打开APP、加载进度、APP页面切换、注册/登录/注销)、2)应用的前后台切换(前后台切换、锁屏、电话、强杀APP再开启、需要处理的提示框时前后台切换、数据交互时前后台切换)、
- 3)免登陆(app有免登陆时考虑版本差异、无网络时是否可免登陆、切换用户登录后,校验登录信息及数据更新,原用户退出、一用户登录另外一设备,原设备退出、app前后台切换、更改密码后,数据交互身份校验、用户退出后,下次是登录界面)、
- 4)数据更新、离线浏览、APP更新、定位/相机服务、时间测试、PUSH测试
- 性能测试
- 交叉事件测试
- 升级、更新测试
- 用户体验测试
- 硬件环境测试
- 客户端数据库测试
- 安全测试
2. 几个主流的框架:
Android UI 自动化的两大类: 1)Android(UI Automator):通过 Android 提供的服务获取当前窗口的视图信息
2)基于 Instrumentation 把测试APK 和被测 APK,运行一个进程中,通过 Java 反射机制获取当前窗口的所有视图,根据该视图获取目标控件的属性
- UI Automator:优点:1)Android 提供,基本支持所有的 Android 事件;2)相对 Instrumentation,不需要了解代码实现细节; 3)基于 Java,测试代码简单;4)跨APP,可打开如相册等其他功能;
- 缺点:只支持 SDK16(Android 4.1)及以上,不支持 Hybrid APP、Web APP
- Espresso(基于 Instrumentation):Google开源自动化框架,相对 Robotium 和 UI Automator,规模小、简洁,测试代码简单,易上手。不能跨 APP
- Selendroid(基于 Instrumentation):可测试 Native APP、Hybrid APP、Web APP。 缺点:资料少
- Robotium (基于 Instrumentation):应用较多。缺点:测试需要有 java 基础,了解 Android 基本租金,不能跨 APP
- Appium:目前主流
7.2 Appium 框架
1. Appium 是开源、跨平台的测试框架,可测试 iOS、Android、Firefox OS 的模拟器和真机。使用 WebDriver 的 JSON wire 协议来驱动 Apple 的 UI Automation 及 Android 的 UI Automator 框架。
Q: 自动化测试过程中,涉及的相关数据有哪些类型及如何处理? 分析:测试过程中涉及到哪些数据?
=> 业务流所需的(测试数据、用户数据)、环境数据、程序中的固定数据(常量数据) 通常,
1)程序中固定数据,一般会设置为常量数据;
2)业务测试数据,一般直接提取分离到相应的数据格式文件(csv/yaml/json/xml),有可以将该部分数据直接从数据库中读取(access/sqlite/mysql/oracle)
3)环境配置相关数据一般会提取到 ini、data、xml 等格式文件
2. Appium 日志分析
# 1. 获取当前模拟器的 android 版本
adb shell getprop ro.build.version.release # 2. 获取当前模拟器的 sdk 版本
adb shell getprop ro.build.version.sdk # 3. 获取当前模拟器的分辨率
adb shell vm size # 4. 获取机型、品牌
adb shell getprop ro.product.model
adb shell getprop ro.product.manufactuer # 5. 如设定 app 参数,实际是将 app 指定路径的 apk 先上传到模拟器或手机中,然后再进行安装;默认的上传路径是: /data/local/tmp/appium_cache # 6. dos 下安装app 命令是 adb install -r xxxx.apk,如进入手机终端其命令是 adb shell pm install -r xxxx.apk
7.3 Desired Capabilities
1. 官方说明文档:http://appium.io/docs/en/writing-running-appium/caps/index.html
2. 如需要与模拟器建立会员,需申明的值有:
desired_caps = {
"platformName": "Android",
"platformVersion": "7.1.2",
"deviceName": "3141a881",
"appPackage": "com.gece.intelligence",
"appActivity": "com.hdme.hdjy.controller.SplashActivity",
"skipServerInstallation": True,
"noReset": True
}
3. 需要注意的事项有:
- 1)capbilities 的键名严格区分大小写,是关键字
- 2)capbilites 的键名不能有空格
- 3)start appium server before create session
- 4)appium-server 与模拟器建立连接时需保证 adb devices 时能正常获取到设备,如提示版本不一致,可将 模拟器路径下的 adb 备份,再讲 android sdk 下的 adb 复制到模拟器路径下
7.4 Appium 元素定位
7.4.1. app 元素定位工具:
- 1)appium-server 的 appium-inspect:appium-server 中新建一个 session,填入对应的 desired_capabilities,打开 app
- 2)android-sdk \ tools \ uiautomatorviewer.bat 【注意:JDK1.8 可以正常使用,JDK17 时会运行不了,后续有方法时再更新】
- 前提:手机/模拟器 与其他连接(如 appium-server)断开,否则被占用
7.4.2. 元素定位方法:
需要注意 app 的类型:
- 1)原生 app: 支持 id,class_name, xpath。 其他5种在 HTML5 上支持
- 2)web app:支持 selenium 的 8 种定位
- 3)混合 app:即 原生+HTML5 的混合应用,定位视情况而定
7.4.3 原生 app 的定位方式
1)id:-> resource-id (app 里 id 一般是唯一的)
self.driver.find_element(By.ID, "com.tencent.mm:id/f3d").click() 2)class_name: -> class (app 里,class_name 一般不唯一,通常使用复数形式)
self.driver.find_elements(By.CLASS_NAME, "android.widget.Buton")[1].click() 3)Xpath
3.1) resource-id 唯一
self.driver.find_element(By.XPATH, "//*[@resource-id='com.tencent.mm:id/fam']").click() 3.2)class 属性唯一
3.2.1)作为标签
self.driver.find_element(By.XPATH, "//android.widget.Button").click()
3.2.2)class 作为属性
self.driver.find_element(By.XPATH, "//*[@class='//android.widget.Button']").click() 3.3)text 值唯一
self.driver.find_element(By.XPATH, "//*[@text='登录']").click() 3.4) contains 模糊匹配
self.driver.find_element(By.XPATH, "//*[contains(@text, '登')]").click() 3.5)组合定位
# 如 class_name 和 id 的组合
id_class = '//android.widget.Button[@resource-id="com.tencent.mm:id/fam"]'
self.driver.find_element(By.XPATH, id_class).click() 3.6) 层级定位
father_son = '//*[@resource-id="com.tencent.mm:id/fam"]/android.widget.Button[1]'
self.driver.find_element(By.XPATH, father_son).click()
7.5 Appium 高级元素定位及扩展
7.5.1. accessibility_id
android 上其属性对应 content-desc;IOS 上对应 label 或 name
self.driver.find_element_by_accessibility_id("More Info").click()
7.5.2. 坐标点定位
一般需要根据 屏幕大小计算相对比例。适应于 实在无法定位的情况下,或跳转第三方软件如微信分享
# driver 下的 tap() 传 1个或多个 (x,y) 元组的列表
self.driver.tap([(x1, y1), (x2, y2)], duration)
7.5.3. uiautomator 定位
1)text 方法,-> 对应 text 属性
# 1)text
login_text = 'text("登录")'
driver.find_element_by_android_uiautomator(login_text).click() # 2)textContains
login_textContains = 'textContains ("录")'
driver.find_element_by_android_uiautomator(login_textContains ).click() # 3)textStartWith
login_textStart = 'textStart("登")'
driver.find_element_by_android_uiautomator(login_textStart).click() # 4)textMatches
login_textMatches = 'textMatches (".*")' // 参数需要传入一个正则表达式
driver.find_element_by_android_uiautomator(login_textMatches ).click()
2)resouceId 方法 -> 对应 resouce-id
id = 'resourceId("com.tencent.mm:id/fam")'
driver.find_element_by_android_uiautomator(id).click()
3)className 方法 -> 对应 class 属性
className = 'className("android.widget.Button")'
driver.find_element_by_android_uiautomator(className).click()
4)组合定位
# 如 resouceId 和 text 组合
IdText = 'resouceId("com.tencent.mm:id/fam").text("登录")'
driver.find_element_by_android_uiautomator(IdText).click() # className 和 text 组合
classText = 'className("android.widget.Button").text("登录")'
driver.find_element_by_android_uiautomator(classText).click()
5)父子定位
# 通过父元素定位子元素:父元素.childSelector(子元素)
fatherChild = 'resourceId("com.tencent.mm:id/fan").childSelector(className("android.widget.Button"))'
driver.find_element_by_android_uiautomator(fatherChild).click()
6)兄弟定位
# 通过同级元素定位同级元素,方式:好定位的兄弟属性.fromParent(较难定位的属性)
brother = 'resourceId("com.tencent.mm:id/faw").fromParent(className("android.widget.Button"))'
driver.find_element_by_android_uiautomator(brother).click()
注意:
1. 所有方法前面都省略了 创建对象
如 login.text = 'text("登录")', 其完整的是:
login.text = 'new UiSelector().text("登录")' 2. 所有方法体内的参数必须是 双引号 "",外面是 单引号 '',原因是 new UiSelector() 是 java 对象,其字符串参数必须 双引号。
======== 最新的 selenium 4.5 语法 =========
from appium import webdriver
from Read_Ini import ReadIni
from appium.webdriver.common.appiumby import AppiumBy
from time import sleep class StartAPP(object): def __init__(self, desired_caps, **kwargs):
self.driver = webdriver.Remote(command_executor="http://%s:%s/wd/hub" % (kwargs["server_address"], kwargs["sever_port"]),
desired_capabilities=desired_caps)
self.driver.implicitly_wait(10) def to_custom_page(self):
sleep(3)
hangqing_icon = 'className("android.widget.TextView").text("行情")'
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, hangqing_icon).click() my = "//android.widget.TextView[@text='我的']"
self.driver.find_element(AppiumBy.XPATH, my).click() if __name__ == '__main__':
appium_config = ReadIni()
ini_path = "app_appium_config.ini" start_app = StartAPP(appium_config.read_ini(ini_path, "desired_capabilities"), **appium_config.read_ini(ini_path, "server_info"))
start_app.to_custom_page()
7)TouchAction 坐标/元素定位
from appium.webdriver.common.appiumby import AppiumBy
from appium.webdriver.common.touch_action import TouchAction
from selenium.webdriver.support.wait import WebDriverWait def qq_login_TouchAction2(self):
# 另外一个方式,该元素可定位,但无法点击。通过 selenium 的 WebDriverWait 的显式等待
# tap() 参数可以是元素,也可以是坐标点
id = 'resourceId("com.tencent.mobileqq:id/btn_login")'
try:
get_element = WebDriverWait(self.driver, 10, 1).until(lambda dir:dir.find_element(AppiumBy.ANDROID_UIAUTOMATOR, id))
TouchAction(self.driver).tap(x=500, y=1700).perform()
except:
print(sys.exc_info())
7.6 Appium API 及对象识别
1. appium 常用的 API
appium 官方文档:http://appium.io/docs/en/about-appium/intro/
driver.launch_app() | 重新启动 desired capabilities 中定义的 app | |
driver.close_app() | 关闭 desired capabilities 中定义的 app | driver.quit() 和 driver.close() 是针对 H5的,原生 app 需要使用 close_app() |
以上两个方法通常是针对初始化的 app 实现启动和关闭操作 | ||
driver.start_activity(app_package, app_activity) driver.active_app(app_package) |
启动一个指定包的 app |
当 app 处于后台运行时,可以用该命令重新唤起 launch_app 是重新启动 app |
driver.terminate_app(app_package) | 结束一个指定包的 app | |
driver.background_app(secs) |
将初始化 app 置于后台 多少秒 其中 -1/null 表示一直处于后台 |
1). 过 n 秒后,会重新回到前台; 2). -1/null 会一直置于后台,可以使用 start_activity() 再将其调用出来 |
3)driver.press_keycode(3) 按下 home 的方式让app 置于后台 | ||
driver.press_keycode(code) driver.long_press() driver.send_keys() |
对应手机的键盘操作 |
1. 对应关系:https://developer.android.com/reference/android/view/KeyEvent (需要FQ) 2. 如需发送中文,需要在 desired capabilities 中定义: unicodeKeyboard = true, resetKeyboard = true |
driver.swipe(start_x, start_y, end_x, end_y, duration) 坐标滑动 driver.scroll(from_element, to_element) 基于元素位置滑动 driver.drag_and_drop(from_element, to_element) 基于元素位置拖拽 |
||
1)tap 单击: TouchAction(driver).tap(element).perform() TouchAction(driver).tap(x, y).perform() 2)press and release TouchAction(driver).press(x, y).perform() TouchAction(driver).press(x, y).release().perform() TouchAction(driver).press(x, y).wait(1000).release().perform() 3)move_to 移动,可用于手势密码操作 TouchAction(driver).press(x,y).move_to(x1, y1).move_to(x2,y2).\ ...release().perform() |
from appium.webdriver.common.touch_action import TouchAction |
7.7 Native 与 WebView
1. 小程序:基于微信或支付宝平台上的 H5 页面的运行方式(依赖浏览器)
2. WebView:基于 WebKit 引擎,采用 chromium (android 4.4+)及 WebKit (< android 4.4)
3. 如何判定当前页面是原生的还是 H5
- 1)原生是 activity 窗口间的切换;H5 是访问页面,会出现加载进度条。如果有进度条则说明有 H5 嵌套
- 2)无网情况下,原生可以正常加载当前页面元素;H5 则无法显示
- 3)如果页面有关闭操作,则基本为 H5
4. 如果需要获取当前程序中的所有 context(上下文)状态,则 app 必须为 debug 状态
如果是使用模拟器,则需要进行如下操作:
- 1)安装逍遥模拟器
- 2)模拟器中安装 xposed 框架(xposed 版本与模拟器的操作系统版本要一致)
- 3)将 webviewhook.apk 组件加载到 框架中
- 4)模拟器中安装 chrome 浏览器,且正常打开
- 5)PC 上 chrome 浏览器输入 chrome://inspect/#devices,至此,app 上如果有 webview 的相关信息,会显示在浏览器中
读后笔记 -- Python 全栈测试开发 Chapter7:移动自动化测试框架的更多相关文章
- 【Python全栈-后端开发】嵩天老师-Django
嵩天老师-Python云端系统开发入门教程(Django) 视频地址:https://www.bilibili.com/video/av19801429 课前知识储备: 一.课程介绍: 分久必合.合久 ...
- 【Python全栈-后端开发】Django入门基础
Django基础知识 一. 什么是web框架? 框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的 ...
- 【Python全栈-后端开发】Django进阶2-Form表单
Django进阶2-Form表单 Django的Form主要具有一下几大功能: 生成HTML标签(可以保留上次输入内容) 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页 ...
- 【Python全栈-后端开发】数据库进阶
数据库进阶 python关于mysql的API---pymysql模块 pymsql是Python中操作MySQL的模块,其使用方法和py2的MySQLdb几乎相同. 模块安装 pip install ...
- 【Python全栈-后端开发】Django进阶之Model操作复习
Django进阶之Model操作复习 一.字段 AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - ...
- 【Python全栈-后端开发】Django进阶1-分页
Django[进阶篇-1 ]分页 分页 一.Django内置分页 from django.core.paginator import Paginator, EmptyPage, PageNotAnIn ...
- 【Python全栈-后端开发】Django入门基础-2
Django入门基础知识-2 一 .模版 一.模版的组成 HTML代码+逻辑控制代码 二.逻辑控制代码的组成 1 变量(使用双大括号来引用变量) {{var_name}} 2 标签(tag)的使用 ...
- 【Python全栈-后端开发】MySQL数据库-练习题
MySQL数据库-练习题 一.表关系 请创建如下表,并创建相关约束 二.操作表 1.自行创建测试数据 2.查询“生物”课程比“物理”课程成绩高的所有学生的学号: 3.查询平均成绩大于60分的同学的学号 ...
- Python 全栈开发 -- 监控篇
如果你已经玩转了 Python 编程语言语法,肯定想用这些知识,开发一款应用程序,它可以是在网上,可以炫耀或出售,那就需要全栈式开发 Python.具体如何创建,部署和运行生产 Python Web ...
- python 全栈开发之路 day1
python 全栈开发之路 day1 本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...
随机推荐
- Redux Toolkit 的使用方法
Redux Toolkit 是什么? Redux Toolkit 是 Redux 官方强烈推荐,开箱即用的一个高效的 Redux 开发工具集.它旨在成为标准的 Redux 逻辑开发模式,我们强烈建议你 ...
- sqllabs:less18-22
在利用抓包工具进行抓包的时候,我们能看到很多的项,下面详细讲解每一项. HTTP 头部详解 1. Accept:告诉 WEB 服务器自己接受什么介质类型,/ 表示任何类型,type/* 表示该类型下的 ...
- (八) Mysql 脑图总结
- java学习日记20230302-字符
JAVA字符 char c1 = 97 System.out.println(c1)// a 会输出97代表的字符(字符编码) 字符类型细节: 字符常量用单引号 java中允许使用\转义字符代表一个字 ...
- 第十一周作业-N67044-张铭扬
1. redis主从复制原理? 1 从节点1.2 2 127.0.0.1:6379> replicaof 10.0.0.157 6379 3 OK 4 127.0.0.1:6379> co ...
- MSB/LSB的意思
MSB一般指最高有效位. 最高有效位( most significant bit,MSB)指的是一个n位二进制数字中的n-1位,具有最高的权值2^(n-1).最低有效位和最高有效位是相对应的概念. L ...
- 加载properties文件
import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java ...
- Word14 互联网络发展状况统计报告office真题
1.课程的讲解之前,先来对题目进行分析,首先需要在考生文件夹下,将Wrod素材.docx文件另存为Word.docx,后续操作均基于此文件,否则不得分. 2.这一步非常的简单,打开下载素材文件,在[文 ...
- PS技能之电子签名+修白牙齿
PS技能 NO.1 电子签名 有时候由于时空的限制,本人无法签字,那么电子签名就有了它的作用啦![注:谨慎使用] 亲试有效的教程,现在就是做笔记的时候啦! 教程链接如下: https://blog.c ...
- Minio整合SpringBoot
Minio整合SpringBoot POM: <dependency> <groupId>io.minio</groupId> <artifactId> ...