Appium新版本引發的一個問題

2023-07-20 12:12:46

Appium新版本引發的一個問題

準備工作

測試程式碼

from appium import webdriver
des_cap = {'platformName': 'android'}
driver = webdriver.Remote(command_executor='http://127.0.0.1:4723/wd/hub',
                          desired_capabilities=des_cap)

測試環境

  • python 3.10,虛擬環境
  • pycharm 2018 community
  • 測試時間 2023-7-20

場景一: 預設安裝 PASS

  • 在pycharm中安裝appium-python-client,版本不指定,此時是2.11.1

  • 對應依賴selenium4.10.0

  • 執行範例程式碼

  • 測試通過

  • ??? 所以博主你要表達啥

  • 繼續看下去

場景二:appium-python-client2.6.0+selenium4.10 FAIL

  • 你根據指定版本安裝appium-python-client為2.6,自動安裝selenium4.10

  • 執行範例程式碼

  • 測試失敗

  • 提示如下

    D:\Appium01\venv\Scripts\python.exe D:/Appium01/demo1.py
    Traceback (most recent call last):
      File "D:\Appium01\demo1.py", line 9, in <module>
        driver = webdriver.Remote(command_executor='http://127.0.0.1:4723/wd/hub',
      File "D:\Appium01\venv\lib\site-packages\appium\webdriver\webdriver.py", line 230, in __init__
        super().__init__(
    TypeError: WebDriver.__init__() got an unexpected keyword argument 'desired_capabilities'
    

場景3:appium-python-client2.6.0+selenium4.3.0 PASS

  • 你應該是先安裝selenium4.3.0
  • 然後再安裝appium-python-client2.6.0
  • 都是指定版本安裝
    • 有同學會說,誰會這樣安裝呢
    • 會的,因為你可能是先學selenium(我課程要求是4.3,最新的版本4.10的改進對我們沒有太大意義,但底層確實改變了很多)
  • 測試通過

問題說明

TypeError 分析

  • 先看報錯

    TypeError: WebDriver.__init__() got an unexpected keyword argument 'desired_capabilities'
    
  • 主要版本資訊:

    • appium-python-client2.6.0
    • selenium4.10
  • 報錯行

    driver = webdriver.Remote
    
  • Remote是個別名

     from .webdriver import WebDriver as Remote
    
  • 看WebDriver原始碼

    class WebDriver(
        webdriver.Remote,
        ActionHelpers,
        Activities,
        Applications,
        Clipboard,
        Context,
        Common,
        DeviceTime,
        Display,
        ExecuteDriver,
        ExecuteMobileCommand,
        Gsm,
        HardwareActions,
        ImagesComparison,
        IME,
        Keyboard,
        Location,
        LogEvent,
        Network,
        Performance,
        Power,
        RemoteFS,
        ScreenRecord,
        Session,
        Settings,
        Sms,
        SystemBars,
    ):
        def __init__(
            self,
            command_executor: str = 'http://127.0.0.1:4444/wd/hub',
            desired_capabilities: Optional[Dict] = None,
            browser_profile: str = None,
            proxy: str = None,
            keep_alive: bool = True,
            direct_connection: bool = True,
            extensions: Optional[List['WebDriver']] = None,
            strict_ssl: bool = True,
            options: Union[AppiumOptions, List[AppiumOptions]] = None,
        ):
    
  • __init__中傳遞了desired_capabilities沒有問題

  • 繼續分析堆疊

    File "D:\Appium01\venv\lib\site-packages\appium\webdriver\webdriver.py", line 230, in __init__
        super().__init__(
    
  • 繼續看WebDriver此處原始碼

            super().__init__(
                command_executor=AppiumConnection(command_executor, keep_alive=keep_alive),
                desired_capabilities=desired_capabilities,
                browser_profile=browser_profile,
                proxy=proxy,
                options=options,
            )
    
  • 這裡也有desired_capabilities,為何報錯了呢

  • 請看WebDriver的繼承webdriver.Remote

    class WebDriver(BaseWebDriver):
        _web_element_cls = WebElement
        _shadowroot_cls = ShadowRoot
    
        def __init__(
            self,
            command_executor="http://127.0.0.1:4444",
            keep_alive=True,
            file_detector=None,
            options: Union[BaseOptions, List[BaseOptions]] = None,
        ) -> None:
    
  • 到這裡你發現了,這個__init__裡面沒有desired_capabilities

  • 注意webdriver.Remote是隸屬於selenium的,你此時的selenium是4.10,升級了,可能導致它remove了一些引數

appium-python-client2.11.1+selenium4.10

  • 這是預設組合,要知道selenium也是4.10了,為何沒有報錯呢?

  • 其呼叫關係簡單分析下

  • Remote__init__中,也支援desired_capabilities,但有如下資訊

            # TODO: Remove the deprecated arg
            desired_capabilities: Optional[Dict] = None,
            
            if desired_capabilities is not None:
                warnings.warn(
                    'desired_capabilities argument is deprecated and will be removed in future versions. '
                    'Use options instead.',
                    DeprecationWarning,
                )
    
    • 後續要移除desired_capabilities
    • 用options替代(模仿selenium)
  • 關鍵的問題是在於,appium-python-client2.11.1中對父類別__init__的呼叫是不攜帶desired_capabilities的

            super().__init__(
                command_executor=command_executor,
                options=dst_options,
            )
    
  • 完整程式碼片段如下

    class WebDriver(
        webdriver.Remote,
        ActionHelpers,
        Activities,
        Applications,
        Clipboard,
        Context,
        Common,
        DeviceTime,
        Display,
        ExecuteDriver,
        ExecuteMobileCommand,
        Gsm,
        HardwareActions,
        ImagesComparison,
        IME,
        Keyboard,
        Location,
        LogEvent,
        Network,
        Performance,
        Power,
        RemoteFS,
        ScreenRecord,
        Session,
        Settings,
        Sms,
        SystemBars,
    ):
        def __init__(
            self,
            command_executor: Union[str, AppiumConnection] = 'http://127.0.0.1:4444/wd/hub',
            # TODO: Remove the deprecated arg
            desired_capabilities: Optional[Dict] = None,
            # TODO: Remove the deprecated arg
            browser_profile: Union[str, None] = None,
            # TODO: Remove the deprecated arg
            proxy: Union[str, None] = None,
            keep_alive: bool = True,
            direct_connection: bool = True,
            extensions: Optional[List['WebDriver']] = None,
            strict_ssl: bool = True,
            options: Union[AppiumOptions, List[AppiumOptions], None] = None,
        ):
            if strict_ssl is False:
                # pylint: disable=E1101
                # noinspection PyPackageRequirements
                import urllib3
    
                # pylint: disable=E1101
                # noinspection PyPackageRequirements
                import urllib3.exceptions
    
                # noinspection PyUnresolvedReferences
                AppiumConnection.set_certificate_bundle_path(None)
                urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    
            if isinstance(command_executor, str):
                command_executor = AppiumConnection(command_executor, keep_alive=keep_alive)
    
            if browser_profile is not None:
                warnings.warn('browser_profile argument is deprecated and has no effect', DeprecationWarning)
    
            if proxy is not None:
                warnings.warn('proxy argument is deprecated and has no effect', DeprecationWarning)
    
            if desired_capabilities is not None:
                warnings.warn(
                    'desired_capabilities argument is deprecated and will be removed in future versions. '
                    'Use options instead.',
                    DeprecationWarning,
                )
            # TODO: Remove the fallback after desired_capabilities removal
            dst_options = (
                AppiumOptions().load_capabilities(desired_capabilities)
                if desired_capabilities is not None and options is None
                else options
            )
    
            super().__init__(
                command_executor=command_executor,
                options=dst_options,
            )
    

appium-python-client2.6.0+selenium4.3.0

  • 想必分析到此處,你應該盲猜能知道為何這個也PASS了

  • 是因為selenium的版本中webdriver.Remote中是有desired_capabilities的

    class WebDriver(BaseWebDriver):
        _web_element_cls = WebElement
        _shadowroot_cls = ShadowRoot
    
        def __init__(self, command_executor='http://127.0.0.1:4444',
                     desired_capabilities=None, browser_profile=None, proxy=None,
                     keep_alive=True, file_detector=None, options: Union[BaseOptions, List[BaseOptions]] = None):
    

總結

  • 最新版本appium-python-client即將不提供desired_capabilities的傳參,但目前能用
  • 在selenium4.10中已經不支援desired_capabilities引數
  • 錯誤的搭配可能會引發上述問題,要麼用最新的版本(預設安裝),要麼2個都用較低的版本
  • 留在最後的問題,那麼在appium最新版中應該如何傳遞能力值呢?
from appium import webdriver
from appium.options.common import AppiumOptions

option = AppiumOptions()
option.set_capability('platformName','android')
driver = webdriver.Remote(command_executor='http://127.0.0.1:4723/wd/hub',
                          options=option)