0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SeleniumのWebElementの出現をauto-waitingする

Last updated at Posted at 2025-05-26

はじめに

やりたいことは、Playwrightのように、ロケータで指定した要素が出現するまで待機する(auto-waiting)ことです。
SeleniumのWebElementは、要素が見つからない場合に例外を投げるため、通常のコードでは自動的に待機しません。
そのため、要素が出現するまで待機するためのラッパーをPage Object Modelの設計パターンに組み込んでで実装します。

環境

  • Python: 3.13
  • Selenium: 4.32.0
  • Appium: 2.18.0

実現方式

Explicit waitsに記述されたWebDriverWaitを使用して、要素が出現するまで待機します。
WebDriverWaitは、指定した条件が満たされるまで待機するためのクラスです。条件には、要素が存在することや、要素がクリック可能であることなどがあります。
ここでは、要素が出現するまで(visibleになるまで)待機するために、expected_conditionsモジュールのvisibility_of_element_locatedを使用します。

実装

基底クラス

以下に示すクラスを継承して、Page Object Modelのクラスを実装します。
Model.find_elementメソッドは、指定したロケータで要素を検索し、要素が出現するまで待機します。

base.py
from selenium.webdriver.support.expected_conditions import visibility_of_element_located,
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.ui import WebDriverWait


class Model:
    def __init__(self, driver: WebDriver, timeout: float = 10.0) -> None:
        self.driver = driver
        self.wait = WebDriverWait(driver, timeout)

    def find_element(self, locator: tuple[str, str]) -> WebElement:
        return self.wait.until(visibility_of_element_located(locator))

    def __getattr__(self, name: str) -> WebElement:
        if hasattr(self.__class__, name.upper()) and isinstance(getattr(self.__class__, name.upper()), tuple[str, str]):
            locator = getattr(self.__class__, name.upper())
            return self.find_element(locator)
        raise AttributeError(f"'{self.__class__.__name__}' has no attribute '{name}'")

継承クラス

Modelクラスでは、object.__getattr__をオーバーライドしています。
Model.__getattr__メソッドは、「呼び出されたクラス属性名をすべて大文字にした変数をModel.finde_elementメソッドに渡し、その返り値をクラス属性として返す」と定義しています。言い換えれば、ロケータをクラス変数としてあらかじめ定義し、それらのロケータが指す要素(WebElement)をクラス属性として取得できるようにしています。
例えば、以下のLoginPageクラスのUSERNAMEPASSWORD, LOGIN_BUTTONのクラス変数は、ロケータ(tuple[str,str])を表し、これらのロケータに該当するWebElementusernamepasswordlogin_buttonのクラス属性として呼び出すことができます。以下の実装ではLoginPage.loginメソッドで、これらの属性を使用してログイン処理を行っています。

login_page.py
from selenium.webdriver.common.by import By
from .base import Model
class LoginPage(Model):
    USERNAME = (By.ID, "username")
    PASSWORD = (By.ID, "password")
    LOGIN_BUTTON = (By.ID, "login-button")

    def login(self, username: str, password: str) -> None:
        self.username.send_keys(username)
        self.password.send_keys(password)
        self.login_button.click()

おわりに

SeleniumにはImplicit waitsもありますが、これは要素の出現以外も含めたすべての待機に適用されるため、意図しない待機が発生してしまう可能性があると思います。ここでの実装のように明示的に待機を指定することで、必要な待機のみを行うことができます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?