环境准备:
获取 app 的信息
- app 入口,两种方式获取:
- 1、通过 logcat 日志获取
- Mac/Linux:
adb logcat ActivityManager:I | grep “cmp"
- Windows:
adb logcat ActivityManager:I | findstr "cmp"
- 2、通过 aapt 获取
- Mac/Linux:
aapt dump badging wework.apk | grep launchable-activity
- Windows:
aapt dump badging wework.apk | findstr launchable-activity
- 启动应用命令
adb shell am start -W -n <package-name>/<activity-name> -S
配置待测应用
-
platformName
:平台,Android/iOS -
deviceName
:设备名 -
appPackage
:应用的包名 -
appActivity
:应用的页面名 Activity -
noReset
: 防止清空缓存信息
Appium inspector 页面功能键
-
SelectElements
:选中元素,查看层级和属性 -
Swipe By Coordinates
:通过坐标点滑动 -
Tap By Coordinates
:通过坐标点点击 -
Back
:返回 -
Refresh Source & Screenshot
:刷新页面 -
StartRecording
:开始录制脚本 -
Search for element
:搜索元素 -
Copy XML Source to Clipboard
:复制 xml 结构 -
Quit Session & Close Inspector
:退出当前 Session
capability 配置参数解析
官方解释:http://appium.io/docs/en/writing-running-appium/caps/
- 功能:配置 Appium 会话,告诉 Appium 服务器需要自动化的平台的应用程序
- 形式:键值对的集合,键对应设置的名称,值对应设置的值
- 主要分为三部分
| 描述 | 值 |
| 使用的手机操作系统 | iOS,Android,或者 Firefox0S |
| 手机操作系统的版本 | 例如 |
| 使用的手机或模拟器类型 |
的 在 Andorid 上虽然这个参数目前已被忽略,但仍然需要添加上该参数 |
| 使用哪个自动化引擎 |
|
| 在当前 session 下不会重置应用的状态。默认值为 |
|
| 连接的真实设备的唯一设备编号 (Unique device identifier) | 例如 |
- 2、ios 部分
| 描述 | 值 |
| 被测应用的 bundle ID 。用于在真实设备中启动测试,也用于使用其他需要 bundle ID 的关键字启动测试。在使用 bundle ID 在真实设备上执行测试时,你可以不提供 | 例如 |
| 当 iOS 的个人信息访问警告 (如 位置、联系人、图片) 出现时,自动选择接受( Accept )。默认值 false | true 或者 false |
| 是否在 appium 日志中显示从设备捕获的任何日志。默认 false | true or false |
- 3、android 部分
| 描述 |
|
| Activity 的名字是指从你的包中所要启动的 Android acticity。他通常需要再前面添加 |
|
| 运行的 Android 应用的包名 |
|
| 用于等待启动的 Android Activity 名称 |
|
| 启用 Unicode 输入,默认为 false |
|
| |
|
| 首次启动的时候,不停止 app |
|
| 跳过安装,权限设置等操作 |
|
用例结构分析
- 添加 capability 信息
- 初始化
webdriver
,添加setup
和teardown
- 添加隐式等待和
noReset
属性增强用例稳定性 - 添加断言
1 import time
2 from appium import webdriver
3 from appium.webdriver.common.mobileby import MobileBy
4
5
6 class TestDemo:
7 def setup(self):
8 desire_cap = {}
9 # 平台
10 desire_cap['platform'] = 'Android'
11 # 设备名
12 desire_cap['deviceName'] = 'emulator'
13 # app 包名
14 desire_cap['appPackage'] = 'io.appium.android.apis'
15 # app 页面名
16 desire_cap['appActivity'] = '.ApiDemos'
17 self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desire_cap)
18 self.driver.implicitly_wait(10)
19
20 def teardown(self):
21 time.sleep(3)
22 # 退出应用
23 self.driver.quit()
24
25 def test_api_demo(self):
26 """
27 1、打开 API demo apk
28 2、点击 OS 控件
29 3、点击 Morse Code 控件
30 4、在搜索框中输入 阿里巴巴
31 5、返回到第一页
32 6、断言
33 :return:
34 """
35 # 点击 OS 控件
36 self.driver.find_element_by_accessibility_id("OS").click()
37 # 点击 Morse Code 控件
38 self.driver.find_element_by_accessibility_id("Morse Code").click()
39 # 输入`阿里巴巴`
40 self.driver.find_element_by_id("io.appium.android.apis:id/text").clear()
41 self.driver.find_element_by_id("io.appium.android.apis:id/text").send_keys("阿里巴巴")
42 # 返回第一页
43 self.driver.back()
44 self.driver.back()
45 self.driver.back()
46 # 选择元素进行断言
47 result = self.driver.find_element(MobileBy.XPATH,"//*[@resource-id='android:id/text1'][1]")
48 print(result.text)
49 # 断言
50 assert result.text == "阿里巴巴"
完整脚本
常见控件定位方法
Selenium提供了八种定位方式:https://www.selenium.dev/documentation/webdriver/elements/locators/
方式 | 描述 |
class name | class 属性对应的值 |
css selector(重点) | css 表达式 |
id(重点) | id 属性对应的值 |
name(重点) | name 属性对应的值 |
link text | 查找其可见文本与搜索值匹配的锚元素 |
partial link text | 查找其可见文本包含搜索值的锚元素。如果多个元素匹配,则只会选择第一个元素。 |
tag name | 标签名称 |
xpath(重点) | xpath表达式 |
driver.find_element(By.ID, "ID属性对应的值")
driver.find_element(By.NAME, "Name属性对应的值")
driver.find_element(By.CSS_SELECTOR, "css表达式")
driver.find_element(By.XPATH, "xpath表达式")
-
driver.find_element(By.LINK_TEXT,"文本信息")
强制等待与隐式等待
原因:避免页面未渲染完成后操作,导致的报错
1、直接等待:time.sleep(3)
解决方案:在报错的元素操作之前添加等待
原理:强制等待,线程休眠一定时间
2、隐式等待:driver.implicitly_wait(3)
解决方案:针对于寻找元素的这个动作,使用隐式等待添加配置。
原理:设置一个等待时间,轮询查找(默认0.5秒)元素是否出现,如果没出现就抛出异常
3、显式等待
- 元素可以找到,使用点击等操作,出现报错
- 原因:
- 页面元素加载是异步加载过程,通常html会先加载完成,js、css其后
- 元素存在与否是由HTML决定,元素的交互是由css或者js决定
- 隐式等待只关注元素能不能找到,不关注元素能否点击或者进行其他的交互
- 示例:
WebDriverWait(driver实例, 最长等待时间, 轮询时间).until(结束条件)
- 如:WebDriverWait(driver, 10).until( '#success_btn')))
- 原理:在最长等待时间内,轮询,是否满足结束条件
类型 |
| 原理 | 适用场景 |
直接等待 |
| 强制线程等待 | 调试代码,临时性添加 |
隐式等待 |
| 在时间范围内,轮询查找元素 | 解决找不到元素问题,无法解决交互问题 |
显式等待 |
| 设定特定的等待条件,轮询操作 | 解决特定条件下的等待问题,比如点击等交互性行为 |
自动化测试定位策略(待补充)
实践练习:
1、用例编写思路
- pytest 测试框架编写
- 添加隐式等待
- 添加 setup teardown
- 添加断言
2、代码示例
1 from appium import webdriver
2 from appium.webdriver.common.mobileby import MobileBy
3
4 # 打开【雪球】应用首页
5 # 点击搜索框(点击之前,判断搜索框的是否可用,并查看搜索框 name 属性值,并获取搜索框坐标,以及它的宽高)
6 # 向搜索框输入:alibaba
7 # 判断【阿里巴巴】是否可见
8 # 如果可见,打印“搜索成功”
9 # 如果不可见,打印“搜索失败
10
11 class TestXueQiu:
12 def setup(self):
13 desired_caps = {}
14 desired_caps['platformName'] = 'Android'
15 desired_caps['platformVersion'] = '10'
16 desired_caps['deviceName'] = 'emulator-5554'
17 desired_caps['appPackage'] = 'com.xueqiu.android'
18 desired_caps['appActivity'] = '.view.WelcomeActivityAlias'
19 desired_caps['noReset'] = 'true'
20 self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
21 self.driver.implicitly_wait(10)
22
23 def teardown(self):
24 self.driver.quit()
25
26 def test_search(self):
27 element = self.driver.find_element(MobileBy.ID,"com.xueqiu.android:id/tv_search")
28 search_enabled = element.is_enabled()
29 print(f"搜索框的文本:{element.text},搜索框的坐标:{element.location},搜索框的size:{element.size}")
30
31 if search_enabled == True:
32 element.click()
33 self.driver.find_element(MobileBy.ID,
34 "com.xueqiu.android:id/search_input_text").\
35 send_keys("alibaba")
36 alibaba_element = self.driver.find_element(MobileBy.XPATH, "//*[@text='阿里巴巴']")
37 # alibaba_element.is_displayed()
38 displayed = alibaba_element.get_attribute("displayed")
39 assert displayed == "true"
40 else:
41 assert False
脚本示例
2023-1-2笔记