adb 调试工具

adb工作原理

概念

adb全面Android Debug Bridge ,是一个调试工具

adb构成和工作原理

adb包含3部分

  • client : 运行在开发机器中,用来发送adb命令
  • daemon守护进程:运行在调试设备中,手机或者模拟器,用来接受并执行adb命令
  • server:同样运行在开发机器中,用来管理client和daemon之间的通信

adb 常用命令

  1. adb devices - 列出当前连接的设备

  2. adb shell - 进入设备的shell命令行界面

  3. adb shell dumpsys window windows - 获取所有包名

    1
    2
    mFocusedApp=AppwindowToken{53309da token=Token{2e2fa785
    ActivityRecord{2928d4fc uo com.android.settings/.settingst 1127333}}}

    包名:com.android.settings (决定唯一性)
    界面名:.settings (一个界面对应一个界面名,类似于命名空间)

  4. adb shell am start -w 包名/界面名 - 获取app启动时间

    • ThisTime:界面启动耗时(ms)
    • TotalTime:应用自身启动耗时 = ThisTime + 应用application等资源启动时间(ms)
    • waitTime:系统启动应用耗时 = TotalTime + 系统资源启动时间(ms)
  5. adb install <apk文件路径> - 安装应用程序到设备

  6. adb uninstall <包名> - 卸载应用程序

  7. adb pull <设备文件路径> <保存到本地的路径> - 从设备中拷贝文件到本地

  8. adb push <本地文件路径> <设备文件路径> - 将本地文件推送到设备

  9. adb logcat - 查看设备的日志信息

    • 关于发生崩溃的时候,需要找到日志中”at” 前面,第一个字符是E的就是错误信息
  10. adb backup - 备份应用程序和数据

  11. adb restore - 恢复应用程序和数据

  12. adb reboot - 重启设备

adb-Monkey

Monkey是Android平台上一个用于自动化压力测试和UI测试的命令行工具。

  1. adb shell monkey -p <包名> <事件数量> - 对指定包名的应用程序执行指定数量的随机事件
  2. adb shell monkey -p <包名> –pct-touch <触摸事件的百分比> –pct-motion <滑动事件的百分比> - 指定触摸事件和滑动事件的百分比,并对应用程序执行随机事件
  3. adb shell monkey -p <包名> -v <事件数量> - 显示详细的事件信息
  4. adb shell monkey -p <包名> -s <种子值> <事件数量> - 使用指定的种子值来执行随机事件
  5. adb shell monkey -p <包名> –throttle <延迟时间> - 设置事件之间的延迟时间,以便模拟用户的操作速度
  6. adb shell monkey -p <包名> -f <脚本文件> <事件数量> - 使用指定的事件脚本文件来执行事件

appium

环境安装

从2022年1月1号开始,Appium核心团队不会再维护Appium 1.x版本了
当您安装Appium 1.x时, 所有可用的驱动程序将与主Appium Server同时安装
在Appium V2版本中驱动和Appium Server是分开的,驱动可以分开安装和升级
安装Appium 2.0 (例如, 通过 npm install -g appium ) , 将只安装Appium Server, 但没有驱动程序. 要安装驱动程序

  1. 安装jdk环境+ sdk环境

  2. 安装appium - 开源跨平台,支持IOS,Android

    • 客户端安装

    • node 包管理工具 npm安装 appium server

      1
      $ npm install appium -g
  3. appium 驱动安装

    • 设置环境变量,跳过chrome 安装(会自动安装,国内下载不了,直接跳过)

    • uiautomator2 - 安装UIAutomator2,用于安卓设备的驱动程序

      1
      $ appium driver install uiautomator2
    • images - 图像识别 (不低于16 node版本)

      1
      $ appium plugin install images
    • XCUITest - 安装XCUITest,用于iOS设备的驱动程序。

      1
      $ appium driver install xcuitest
    • 检查安装 - appium driver list

  4. 安装appium-inspector - appium可视化界面(主要是用于元素定位) ; 或者使用web版Appium Inspector by Appium Pro,但需要注意开启cors跨域 –allow-cors

  5. 模拟器

  6. 第三方库appium-python-client

appium 2.0 使用

appium启动命令
1
2
# 启动appium 并使用images插件
$ appium --use-plugins=images --use-driver=uiautomator2
基础python代码 - 图片识别案例
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
import os
import time

from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
import base64 # 图像识别需要用到的工具

capabilities = dict(
platformName='Android', # 手机平台 不限大小写
automationName='uiautomator2', # appium2.0 插件
deviceName='emulator-5554', # 设备号
# 打开app或者其他包(这里是打开设置);appium2.0 可以不传;默认当前页面
# appPackage='com.android.settings', # 包名
# appActivity='.Settings', # 界面名
)

# 实例化对象
appium_server_url = 'http://localhost:4723'

# 创建远程驱动,并加载配置 appium2.0 新写法
driver = webdriver.Remote(appium_server_url, options=UiAutomator2Options().load_capabilities(capabilities))

# 更新driver实例图像处理设置 - 使用images插件(识别图片)
driver.update_settings({
'fixImageFindScreenshotDims': False,
'fixImageTemplateSize': True,
'autoUpdateImageElementPosition': True # 自动更新图像元素位置

})

# 识别图像操作
path = os.getcwd()
print(path)
base64_str = base64.b64encode(open(path + '/tests/test01/img_1.png', 'rb').read())
base64_str = base64_str.decode('utf-8')
# 通过图像定位
driver.find_element(AppiumBy.IMAGE, base64_str).click()
time.sleep(3)
driver.quit()

基础API

应用跳转
1
2
3
4
5
driver.start_activity(appPackage, appActivity)
"""
appPackage:包名
appActivity:界面名
"""
元素定位
  • ID: 通过元素的唯一标识符进行定位。
  • XPath: 通过 XML 路径语言定位元素。XPath 允许使用复杂的路径表达式来查找元素。
  • Accessibility ID: 使用可访问性标识符定位元素,特别适合于 iOS 和 Android 应用。
  • Class Name: 根据元素的类名定位。
  • Name: 通过元素的名字定位,这通常用于 iOS 应用。
  • CSS Selector: 使用 CSS 选择器定位元素(主要用于 Web 测试)。
  • Android UI Automator: 适用于 Android 应用的特定定位方式。
  • iOS Predicate: 适用于 iOS 应用的复杂查询。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from appium.webdriver.common.appiumby import AppiumBy

# id 定位
driver.find_element(AppiumBu.ID, "com.youdao.not:id/search")

# XPATH 定位
driver.find_element(AppiumBy.XPATH, "//android.widget.FrameLayout[@resource-id='com.youdao.note:id/search']")
# 通过XPATH定位文本
driver.find_element(AppiumBy.XPATH, "//*[@text='网络和互联网']")

# CLASS_NAME class类名
driver.find_element(AppiumBy.CLASS_NAME, "android.widget.FrameLayout")

# ACCESSIBILITY_ID
driver.find_element(AppiumBy.ACCESSIBILITY_ID, "更多")

# IMAGE 需要安装插件images
base64_str = base64.b64encode() # 把图片转成base64
driver.find_element(AppiumBy.IMAGE, base64_str)

# 获取多个元素
elements = driver.find_elements(AppiumBu.ID, "com.youdao.not:id/search")
元素操作
  • 点击 (Click): 使用 .click() 方法模拟用户点击。
  • 输入 (Send Keys): 使用 .send_keys() 方法输入文本。
  • 获取文本 (Get Text): 使用 .text 属性获取元素的文本内容。
  • 获取属性 (Get Attribute): 使用 .get_attribute() 方法获取元素的属性值。
  • 获取元素尺寸 (Get Size): 使用 .size 属性获取元素的尺寸。
  • 获取位置 (Get Location): 使用 .location 属性获取元素的位置坐标。
  • 拖动 (Drag and Drop): 通过拖放操作可以模拟用户的拖动行为。
  • 滑动 (Swipe): 在触摸屏设备上,可以通过手势操作模拟滑动。
  • 捏合(Pinch) : 两个手指的放大或缩小手势

可以对拖动,滑动,捏合进行封装,能够达到简写方式

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 元素点击
driver.click()

# 文本框输入
driver.send_keys('hello word')

# 获取文本
driver.text

# 获取属性
driver.get_attribute('content-desc')

# 获取元素尺寸 返回字典 {width: 100,height:50}
driver.size

# 获取位置 返回字典 {x: 10, y: 10}
driver.location

# 拖动元素
'''
拖动操作:
长按(Long Press): 在源元素上长按。
移动(Move To): 将手指从源元素移动到目标元素的位置。
释放(Release): 在目标元素上释放手指,完成拖动。'''
# 定位源元素和目标元素的坐标
source_element = driver.find_element(MobileBy.ID, 'com.example:id/source')
target_element = driver.find_element(MobileBy.ID, 'com.example:id/target')
from appium.webdriver.common.touch_action import TouchAction
# 创建 TouchAction 实例
actions = TouchAction(driver)
# 定义拖动动作
'''
long_press(source_element) 在源元素上长按。
move_to(target_element) 将手指移动到目标元素。
release() 释放手指。
perform():执行整个操作链。'''
actions.long_press(source_element).move_to(target_element).release().perform()

# 滑动元素
'''
按压(Press): 在起始位置按下手指。
移动到(Move To): 将手指从起始位置移动到结束位置。
释放(Release): 释放手指,完成滑动。'''
# 定位起始元素和结束元素
start_element = driver.find_element(MobileBy.ID, 'com.example:id/start')
end_element = driver.find_element(MobileBy.ID, 'com.example:id/end')
# 创建 TouchAction 实例
actions = TouchAction(driver)
# 定义滑动动作
'''
press(start_element) 在起始元素上按下手指。
wait(1000) 等待 1000 毫秒(可选的,用于模拟真实用户的停顿时间)。
move_to(end_element) 将手指滑动到结束位置。
release() 释放手指。
perform():执行整个操作链。'''
actions.press(start_element).wait(1000).move_to(end_element).release().perform()

# 捏合操作 - 放大,缩小
# 创建 TouchAction 实例
action = TouchAction(driver)
# 捏合操作示例
element = driver.find_element(MobileBy.ID, 'com.example:id/zoomable')
# 捏合(两个点)
'''
press() 和 move_to() 用于模拟手势的起始和结束位置。
release() 结束手势操作。
perform() 执行定义的手势。
'''
action.press(x=300, y=300).move_to(x=250, y=250).release().press(x=400, y=400).move_to(x=450, y=450).release().perform()
动态元素
  • 等待 (Wait): 使用显式等待 (WebDriverWait) 或隐式等待来等待元素出现或满足条件。

    • 显式等待 (WebDriverWait): 显式等待是在代码中定义等待条件,直到某个条件成立或者超时

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      from selenium.webdriver.support.ui import WebDriverWait  
      from selenium.webdriver.support import expected_conditions as EC
      from selenium.webdriver.common.by import By
      '''
      WebDriverWait(driver, 10) 创建一个等待实例,最多等待 10 秒。
      EC.visibility_of_element_located 是等待条件,表示等待指定 ID 的元素可见。
      wait.until() 方法会等待条件满足或超时。
      '''
      # 创建显式等待示例
      wait = WebDriverWait(driver, 10) # 最多等待 10 秒
      # 等待元素可见
      element = wait.until(EC.visibility_of_element_located((MobileBy.ID, 'com.example:id/dynamicElementId')))
      # 当元素可见时进行操作
      element.click()

      # 简写
      element = WebDriverWait(driver, 10).until(
      EC.presence_of_element_located((By.ID, "element_id"))
      ).click()
    • 隐式等待:隐式等待是在查找元素时,Appium 会在指定的时间内等待元素出现。隐式等待通常设置一次,然后应用于所有的元素查找操作。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      '''
      driver.implicitly_wait(10) 设置driver隐式等待时间为10秒,后期都生效
      在查找元素时,如果元素没有立即找到,Appium 将会等待最多 10 秒。
      '''
      # 设置隐式等待时间
      driver.implicitly_wait(10) # 最多等待 10 秒
      # 查找元素
      element = driver.find_element(MobileBy.ID, 'com.example:id/dynamicElementId')
      # 进行操作
      element.click()
    • 强制等待: 直接使用sleep() , 让程序暂停一段时间

      1
      2
      import time
      time.sleep(1) # 单位s
  • 轮询 (Polling): 通过不断循环来判断元素状态来处理可能延迟出现的元素。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # 轮询检查元素是否可见  
    timeout = 10
    start_time = time.time() # 记录时间点

    while True:
    try:
    element = driver.find_element(MobileBy.ID, 'com.example:id/dynamicElementId')
    if element.is_displayed():
    print("Element is visible")
    break
    except:
    pass

    elapsed_time = time.time() - start_time # 当超过时间点,则跳出循环
    if elapsed_time > timeout:
    print("Timeout waiting for element")
    break

    time.sleep(0.5) # 每 0.5 秒轮询一次
  • 重试 (Retry): 在执行操作之前重试定位,以应对元素状态变化。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    # 定义重试次数  
    max_retries = 5 # 最大重试次数
    attempts = 0 # 当前重试次数
    element = None # 记录元素的变量

    while attempts < max_retries:
    try:
    # 尝试定位元素
    element = driver.find_element(MobileBy.ID, 'com.example:id/dynamicElementId')
    if element.is_displayed():
    print("Element found and is visible")
    break
    except NoSuchElementException:
    print("Element not found, retrying...")
    attempts += 1
    time.sleep(2) # 等待 2 秒后重试

    if not element: # 当变量还是为None时,则提出错误
    print("Failed to find element after several retries")

    # 如果元素进行操作
    if element:
    element.click()
元素交互
  • 链式操作 (Chained Actions): 通过 Action Chains 支持多步操作,如拖放、点击和移动。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from appium.webdriver.common.touch_action import TouchAction 
    # 创建 TouchAction 实例
    action = TouchAction(driver)
    # 链式操作: 拖放
    action.press(source_element).wait(1000).move_to(target_element).release().perform()
    '''
    TouchAction 用于定义手势操作。
    press():按下源元素。
    wait(1000):等待 1000 毫秒。
    move_to():移动到目标元素。
    release():释放动作。
    perform():执行整个操作链。
    '''
  • 手势操作 (Gesture): 支持高级手势操作,如捏合滑动等。

  • 上下文切换 (Context Switch): 在混合应用(如包含 WebView 的应用类似于web的iframe)中切换上下文来执行操作。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    contexts = driver.contexts  
    print(contexts) # 输出上下文列表

    # 切换到 WebView 上下文
    driver.switch_to.context('WEBVIEW_com.example')

    # 执行 Web 操作
    web_element = driver.find_element(MobileBy.CSS_SELECTOR, 'button')
    web_element.click()

    # 切换回原生上下文
    driver.switch_to.context('NATIVE_APP')
应用操作

源码:~/appium/webdriver/extensions/application.py

  • driver.background_app(time) - 将应用挂入后台一段时间
  • driver.is_app_installed(app_id) - 检查是否安装了某个应用程序
  • driver.install_app(path) - 安装应用程序
  • driver.remove_app(app_id) - 删除应用程序
  • driver.terminate_app(app_id) - 终止程序
  • driver.activate_app(app_id) - 启动或者将后台唤醒
  • driver.query_app_state(app_id) - 检查程序状态
  • driver.driver.current_url() - 获取当前url
  • driver.current_window_handle() - 返回当前窗口句柄
  • driver.window_handles() - 返回所有句柄
  • driver.close() - 关闭当前窗口
  • driver.quit() - 退出程序驱动,并关闭全部窗口
  • driver.save_screenshot(filename) - 将当前窗口保存为png格式图片,返回布尔值
  • driver.get_screenshot_as_png() - 以二进制数据形式返回当前窗口截图
  • driver.get_screenshot_as_base64() - 以base64数据形式返回当前窗口截图
  • driver.execute_scipt() - 执行javascript代码; 例如返回设备信息:device_info = driver.execute_script(“mobile: deviceInfo”) ;移动到指定坐标:driver.execute_script(‘mobile: touch:tap’, {‘x’: 100, ‘y’: 200}) ; 获取网络状态:network_status = driver.execute_script(‘mobile: networkConnection’)
  • session_id = driver.session_id - 获取会话id
appium服务器操作
  • get_status() - 获取appium服务器状态
  • start_session() - 创建一个含有新功能的新会话
appium2.0 新特性和扩展
  • 插件架构 (Plugin Architecture): 允许用户添加自定义插件来扩展功能。

    插件架构是 Appium 2.0 的一项重要特性,它允许用户通过安装自定义插件来扩展 Appium 的功能。插件可以添加新的命令、修改现有功能,或者与其他工具和服务进行集成。

    • 可扩展性: 通过插件架构,用户可以根据特定的需求扩展 Appium 的功能,而不需要直接修改 Appium 的核心代码。

    • 社区插件: Appium 社区和开发者可以发布和分享插件,使得其他用户可以利用这些插件来满足特定的测试需求。

    • 插件管理: 提供了方便的命令行工具来安装、卸载和管理插件。

    • 插件安装

      • 安装插件:
      1
      appium plugin install <plugin-name>  
      1. 列出已安装插件:
      1
      appium plugin list  
      1. 卸载插件:
      1
      appium plugin uninstall <plugin-name>  
  • 增强的 CLI (Command Line Interface): 提供了更强大的命令行工具,便于操作和调试。

Appium 2.0 的 CLI 工具进行了显著的增强,使得用户可以更方便地操作和调试 Appium 服务器及其相关功能

  • 常用命令示例

    启动 Appium 服务器:

    1
    appium  

    启动带有特定配置的 Appium 服务器:

    1
    appium --use-plugins <plugin-name>  

    查看 CLI 选项:

    1
    appium --help  
  • 改进的服务器配置 (Server Configuration): 提供更灵活的配置选项,以适应不同的测试需求。

    • 使用环境变量配置:

      1
      2
      export APPIUM_LOG_LEVEL=debug  
      appium
    • 使用配置文件:

    可以创建一个 JSON 格式的配置文件 appium-config.json 来定义服务器参数。例如:

    1
    2
    3
    4
    5
    {  
    "logLevel": "debug",
    "port": 4723,
    "usePlugins": ["appium-device-config-plugin"]
    }

    然后通过以下命令启动 Appium 服务器:

    1
    appium --config appium-config.json  
    • 示例:

      如果你需要调整日志级别和启用某些插件,可以通过环境变量和配置文件来实现。例如,将日志级别设置为 debug 并启用 appium-device-config-plugin 插件:

      1
      2
      export APPIUM_LOG_LEVEL=debug  
      appium --use-plugins appium-device-config-plugin

对于Android一些特别包名和操作

设置 (Settings)

1
2
appPackage = 'com.android.settings'  
appActivity = '.Settings'

通讯录 (Contacts)

1
2
appPackage = 'com.android.contacts'  
appActivity = '.activities.PeopleActivity'

电子邮件 (Email)

1
2
appPackage = 'com.android.email'  
appActivity = 'com.android.email.activity.Welcome'

短信 (SMS)

1
2
appPackage = 'com.android.mms'  
appActivity = '.ui.ConversationList'

电话 (Dialer)

1
2
appPackage = 'com.android.dialer'  
appActivity = '.DialtactsActivity'

设置为主屏幕 (Home)

如果您想直接返回主屏幕,您可以使用以下代码:

1
driver.start_activity(package='com.android.launcher', activity='.Launcher')  

示例:返回主屏幕

如果您想在 Appium 测试完成后返回主屏幕,可以使用以下示例代码:

1
2
3
4
5
# 返回主屏幕  
driver.press_keycode(3) # 3 是 Home 键的键码

# 或者使用 start_activity 方法
driver.start_activity(package='com.android.launcher', activity='.Launcher')

使用 start_activity

也可以用 start_activity 来直接启动特定的应用:

1
driver.start_activity(appPackage, appActivity)