You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

更新Appium及Python客户端后移动端测试脚本报错求助

Appium 2.5.2 + appium-python-client 4.0.0 版本更新后脚本报错解决方案

问题背景

更新Appium至2.5.2、appium-python-client至4.0.0后,原有移动端Web测试脚本出现两类错误:

  1. 初始错误:ModuleNotFoundError: No module named 'appium.webdriver.common.mobileby'
  2. 替换MobileBy为By后新错误:AttributeError: 'NoneType' object has no attribute 'to_capabilities'

错误原因及修复步骤

1. MobileBy模块找不到的问题

appium-python-client 4.x版本重构了定位方式模块,MobileBy已迁移至appium.webdriver.common.appiumby模块并更名为AppiumBy。直接使用selenium的By类会导致无法识别ANDROID_UIAUTOMATOR这类移动端专属定位策略。

修复方法:

  • 替换导入语句:
    from appium.webdriver.common.appiumby import AppiumBy
    
  • 代码中所有MobileBy或错误使用的By替换为AppiumBy,例如:
    connections = WebDriverWait(driver, 7).until(EC.presence_of_element_located((AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Connections")')))
    

2. 'NoneType' object has no attribute 'to_capabilities'错误

appium-python-client 4.x完全对齐Selenium 4的API规范,创建驱动时需要用AppiumOptions系列类封装capabilities,不再直接传入字典。此外原代码存在一处逻辑错误:在elif airplane_mode_status == 0分支中,调用toggle_airplane_mode时传入的是setup_settings_driver(device_id)(重新创建了一个驱动实例),而非已初始化的settings_driver,这会导致资源重复创建且可能引发异常。

修复方法:

  1. 导入对应平台的Options类:
    from appium.options.android import UiAutomator2Options
    
  2. 修改驱动创建逻辑,用UiAutomator2Options封装capabilities(注意Appium 2.x服务器端点不再是/wd/hub):
    以setup_settings_driver为例:
    def setup_settings_driver(device_id):
        settings_options = UiAutomator2Options()
        settings_options.set_capability('platformName', 'Android')
        settings_options.set_capability('deviceName', device_id)
        settings_options.set_capability('appPackage', 'com.android.settings')
        settings_options.set_capability('appActivity', '.Settings')
        settings_options.set_capability('noReset', True)
        settings_options.set_capability('newCommandTimeout', 200)
        logging.debug("Setting up driver for device settings")
        return webdriver.Remote('http://127.0.0.1:4723', settings_options)
    
  3. 修复elif分支的逻辑错误:
    elif airplane_mode_status == 0:
        settings_driver = setup_settings_driver(device_id)
        toggle_airplane_mode(settings_driver)  # 传入已创建的driver实例,而非重新创建
        settings_driver.quit()
    

完整修复后的代码

import logging
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.action_chains import ActionChains
from appium.options.android import UiAutomator2Options
import subprocess
import time
import random

# 日志配置
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

def check_airplane_mode(device_id):
    # 检查设备的飞行模式状态
    logging.debug(f"Checking airplane mode status for device {device_id}")
    command = ["adb", "-s", device_id, "shell", "settings", "get", "global", "airplane_mode_on"]
    result = subprocess.check_output(command).decode().strip()
    logging.debug(f"Airplane mode status: {result}")
    return int(result)

def setup_settings_driver(device_id):
    # 为设备设置配置驱动
    settings_options = UiAutomator2Options()
    settings_options.set_capability('platformName', 'Android')
    settings_options.set_capability('deviceName', device_id)
    settings_options.set_capability('appPackage', 'com.android.settings')
    settings_options.set_capability('appActivity', '.Settings')
    settings_options.set_capability('noReset', True)
    settings_options.set_capability('newCommandTimeout', 200)
    logging.debug("Setting up driver for device settings")
    return webdriver.Remote('http://127.0.0.1:4723', settings_options)

def toggle_airplane_mode(driver):
    # 切换飞行模式
    logging.debug("Toggling airplane mode")
    driver.start_activity('com.android.settings', '.Settings')
    connections = WebDriverWait(driver, 7).until(EC.presence_of_element_located((AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Connections")')))
    connections.click()
    airplane_mode = WebDriverWait(driver, 7).until(EC.presence_of_element_located((AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("Airplane mode")')))
    airplane_mode.click()
    toggle_switch = WebDriverWait(driver, 7).until(EC.presence_of_element_located((AppiumBy.ID, 'com.android.settings:id/switch_widget')))
    toggle_switch.click()
    time.sleep(2)

def setup_chrome_driver(device_id):
    # 配置Chrome驱动
    chrome_options = UiAutomator2Options()
    chrome_options.set_capability('platformName', 'Android')
    chrome_options.set_capability('deviceName', device_id)
    chrome_options.set_capability('browserName', 'Chrome')
    chrome_options.set_capability('noReset', True)
    chrome_options.set_capability('newCommandTimeout', 200)
    logging.debug("Setting up Chrome driver")
    try:
        driver = webdriver.Remote('http://127.0.0.1:4723', chrome_options)
        return driver
    except Exception as e:
        logging.warning(f"Failed to create driver with UiAutomator2: {e}")
        return None

def random_scroll(driver, min_scroll, max_scroll, min_duration, max_duration):
    # 在网页上执行随机滚动
    width = driver.get_window_size()['width']
    height = driver.get_window_size()['height']

    start_x = width // 2  
    start_y = int(height * 0.8) 
    end_y = int(height * 0.2)
    
    action = ActionChains(driver)
    for _ in range(random.randint(min_scroll, max_scroll)):
        action.move_to_element_with_offset(driver.find_element(AppiumBy.TAG_NAME, "body"), start_x, start_y)
        action.click_and_hold()
        action.move_by_offset(0, end_y - start_y)
        action.release()
        action.perform()
        time.sleep(random.randint(min_duration, max_duration) / 1000)  

def open_chrome_and_search(driver, query):
    # 打开Chrome浏览器并执行搜索
    driver.get("https://www.google.com")
    time.sleep(2)  
    random_scroll(driver, 1, 3, 300, 800)

def get_connected_devices():
    # 获取已连接设备列表
    logging.debug("Fetching list of connected devices")
    result = subprocess.check_output(["adb", "devices"]).decode().splitlines()
    devices = [line.split()[0] for line in result if "device" in line and not "devices" in line]
    return devices

print("Appium version: 2.5.2")

devices = get_connected_devices()
logging.info(f"Connected devices: {devices}")

for device_id in devices:
    # 根据飞行模式状态执行对应操作
    airplane_mode_status = check_airplane_mode(device_id)

    if airplane_mode_status == 1:
        settings_driver = setup_settings_driver(device_id)
        toggle_airplane_mode(settings_driver)
        settings_driver.quit()
        chrome_driver = setup_chrome_driver(device_id)
        if chrome_driver:
            open_chrome_and_search(chrome_driver, "test")
            chrome_driver.quit()
        
    elif airplane_mode_status == 0:
        settings_driver = setup_settings_driver(device_id)
        toggle_airplane_mode(settings_driver)
        settings_driver.quit()

额外注意事项

  • Appium 2.x需要手动安装驱动,执行appium driver install uiautomator2确保UiAutomator2驱动已安装
  • 确认Python环境依赖正确,执行pip list检查appium-python-client版本为4.x,selenium版本为4.x(需与appium-python-client兼容)
  • 设备需开启USB调试,且adb连接正常

内容的提问来源于stack exchange,提问作者斎藤大輔

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 03:04:58