1. 程式人生 > >1127UI自動化測試經驗分享-顯式等待(一)WebDriverWait類、until()方法

1127UI自動化測試經驗分享-顯式等待(一)WebDriverWait類、until()方法

最近忙於其他事情,部落格就沒那麼多時間來寫。原本想先分享下三種等待方式,但是隱式等待我還有點不太懂。這次先分享顯式等待。

一)顯式等待 WebDriverWait()

顯示等待,是針對於某個特定的元素設定的等待時間。

原始碼:

POLL_FREQUENCY = 0.5  # How long to sleep inbetween calls to the method
IGNORED_EXCEPTIONS = (NoSuchElementException,)  # exceptions ignored during calls to the method


class WebDriverWait(object):
    def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None):
        """Constructor, takes a WebDriver instance and timeout in seconds.

           :Args:
            - driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote)
            - timeout - Number of seconds before timing out
            - poll_frequency - sleep interval between calls
              By default, it is 0.5 second.
            - ignored_exceptions - iterable structure of exception classes ignored during calls.
              By default, it contains NoSuchElementException only.

           Example:
            from selenium.webdriver.support.ui import WebDriverWait \n
            element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId")) \n
            is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).\ \n
                        until_not(lambda x: x.find_element_by_id("someId").is_displayed())
        """
        self._driver = driver
        self._timeout = timeout
        self._poll = poll_frequency
        # avoid the divide by zero
        if self._poll == 0:
            self._poll = POLL_FREQUENCY
        exceptions = list(IGNORED_EXCEPTIONS)
        if ignored_exceptions is not None:
            try:
                exceptions.extend(iter(ignored_exceptions))
            except TypeError:  # ignored_exceptions is not iterable
                exceptions.append(ignored_exceptions)
        self._ignored_exceptions = tuple(exceptions)

通過註釋瞭解到:ignored_exceptions:呼叫方法時忽略的異常;poll_frequency:在呼叫方法之間睡多久;

init()方法需要傳參 driver、timeout、poll_frequency、ignored_exceptions;
driver:webdriver的驅動;
timeout:最長超時時間,預設以秒為單位;
poll_frequency:休眠時間(步長)的間隔,檢測間隔時間,預設0.5s;
ignored_exceptions: 超時後的異常資訊,預設情況下拋 “NoSuchElementException"異常;

在設定時間timeout內,每隔一段時間poll_frequency(預設0.5秒) 檢測一次當前頁面,元素是否存在,如果超過設定時間還檢測不到則丟擲異常ignored_exceptions,如果元素存在則立即反饋。

那要怎麼用呢?
看下官方的舉例:element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id(“someId”))

is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).until_not(lambda x: x.find_element_by_id(“someId”).is_displayed())

二)until()、until_not()

WebDriverWait 一般是配合until() 或 until_not()方法,就能夠根據判斷條件而靈活地等待了。主要的意思就是:程式每隔xx秒看一眼,如果條件成立了,則執行下一步;否則繼續等待,直到超過設定的最長時間,然後丟擲TimeoutException異常。

格式:
WebDriverWait(driver, timeout).until(method, message=’’)
WebDriverWait(driver, timeout).until_not(method, message=’’)

【until_not() 我沒用過,本篇就分享until()】

原始碼:

    def until(self, method, message=''):
        """Calls the method provided with the driver as an argument until the \
        return value is not False."""
        screen = None
        stacktrace = None

        end_time = time.time() + self._timeout
        while True:
            try:
                value = method(self._driver)
                if value:
                    return value
            except self._ignored_exceptions as exc:
                screen = getattr(exc, 'screen', None)
                stacktrace = getattr(exc, 'stacktrace', None)
            time.sleep(self._poll)
            if time.time() > end_time:
                break
        raise TimeoutException(message, screen, stacktrace)

    def until_not(self, method, message=''):
        """Calls the method provided with the driver as an argument until the \
        return value is False."""
        end_time = time.time() + self._timeout
        while True:
            try:
                value = method(self._driver)
                if not value:
                    return value
            except self._ignored_exceptions:
                return True
            time.sleep(self._poll)
            if time.time() > end_time:
                break
        raise TimeoutException(message)

until(method,message=’’) 的註釋:Calls the method provided with the driver as an argument until the return value is not False 將驅動提供的方法作為引數呼叫,直到返回值不為False;

好繞啊,要瘋啦,
我覺得就是在說:呼叫method作為一個引數,直到返回值為True;

那這個引數(或者叫條件) value = method(self._driver) 到底是什麼,要怎樣用呢?

不妨回到起點,顯式等待到底是什麼?
顯式等待會讓WebDriver等待滿足一定的條件以後再進一步的執行。 這 滿足一定的條件 是不是value?我覺得 就是。

在自動化測試中,最基本的、最重要的 要滿足的條件就是查詢到元素,find element;
所以value 應該可以寫成driver.find_element(),對吧?

    def test_56(self):
        """ 可執行方法method引數,很多人傳入了WebElement物件 這是錯誤的"""
        from selenium.webdriver.support.wait import WebDriverWait

        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.implicitly_wait(5)
        self.driver.get("https://www.baidu.com")
        WebDriverWait(self.driver, 10).until(self.driver.find_element_by_id('kw'))  # 錯誤

        time.sleep(2)
        self.driver.quit()

但 報錯了。

  File "D:\oss_1\Common\yidu.py", line 3900, in test_56
    WebDriverWait(self.driver, 10).until(self.driver.find_element_by_id('kw'))  # 錯誤
  File "C:\Users\admin\AppData\Local\Programs\Python\Python36-32\lib\site-packages\selenium\webdriver\support\wait.py", line 71, in until
    value = method(self._driver)
TypeError: 'WebElement' object is not callable

說的是 ‘WebElement’ object is not callable;所以 這裡的引數一定要是可以呼叫的,即這個物件一定有 call() 方法,否則會丟擲異常。

如何處理呢? 一般採用匿名函式lambda 。
lambda driver:driver.find_element(<定位元素>)
當定位到元素時返回為WebElement,找不到元素時為顯式等待 報錯】(這兒後面會繼續深入分享)

    def test_57z3(self):
        from selenium.webdriver.support.wait import WebDriverWait

        driver = webdriver.Chrome()
        driver.maximize_window()
        driver.get("https://www.baidu.com")
        print('0', time.ctime())

        # TypeError: 'WebElement' object is not callable
        try:
            WebDriverWait(driver, 10).until(driver.find_element_by_id('kw'), '失敗')
        except Exception as e11:
            print(e11)
        print('1', time.ctime())

        # 成功
        try:
            WebDriverWait(driver, 10).until(lambda the_driver: the_driver.find_element_by_id('kw'), '失敗')
        except Exception as e33:
            print(e33)
        print('3', time.ctime())

        time.sleep(1)
        driver.quit()

這個用例就說明了 顯式等待和until()結合後,可以智慧查詢元素;但是在web中有些元素是隱藏的,確實是存在;只不過不顯示,那就不方便執行操作,要怎麼做一個可以判斷元素顯示與否的顯式等待呢?

用WebElement的 is_displayed() 、is_enabled()、is_selected() 方法
WebDriverWait(driver, 10).until(lambda the_driver: the_driver.find_element_by_id(‘kw’).is_displayed())

    def test_57z5(self):
        from selenium.webdriver.support.wait import WebDriverWait
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get("https://www.12306.cn/index/")
        print('開始', time.ctime())

        # <input id="toStation" type="hidden" value="" name="to_station">
        # 這個元素預設是隱藏的

        # selenium.common.exceptions.TimeoutException: Message: 失敗
        try:
            WebDriverWait(self.driver, 10).until(lambda the_driver: the_driver.find_element_by_id('toStation').is_displayed(), '失敗')
        except Exception as e33:
            print(e33)  # 顯式等待的10秒      Message: 失敗
        print('0', time.ctime())

        # 成功
        try:
            WebDriverWait(self.driver, 10).until(lambda the_driver: the_driver.find_element_by_id('toStation'), '失敗')
        except Exception as e33:
            print(e33)
        print('1', time.ctime())

        time.sleep(1)
        self.driver.quit()

結果很明顯:

Launching unittests with arguments python -m unittest yidu.Test_yidu.test_57z5 in D:\oss_1\Common
開始 Tue Nov 27 10:12:12 2018
Message: 失敗

0 Tue Nov 27 10:12:23 2018
1 Tue Nov 27 10:12:23 2018

Ran 1 test in 21.744s

OK

所以一種很簡單的 把元素查詢作為條件的 顯式等待就出來了,這是可以直接用到自動化指令碼中。起碼比起強制等待time.sleep()要智慧很多的。

下一次分享 顯式等待(二)expected_conditions類

交流技術 歡迎+QQ 153132336 zy