Seleniium 是相當不錯的一個第三方測試框架,可惜目前國內已經無法訪問其官網(翻牆可以)。
不知道大家是否有認真查看過selenium 的api,我是有認真學習過的。不得不說,selenium模塊的文檔組織是非常優秀的,事后多年回想起,我python編碼能力的提升主要是得益於認真拜讀過selenium的源碼。
selenium 的api中包含有WebDriverWait 和 expected_conditions這兩個高級應用。
下面先看WebDriverWait :
1 import time 2 from selenium.common.exceptions import NoSuchElementException 3 from selenium.common.exceptions import TimeoutException 4 5 POLL_FREQUENCY = 0.5 # How long to sleep inbetween calls to the method 6 IGNORED_EXCEPTIONS = (NoSuchElementException,) # exceptions ignored during calls to the method 7 8 9 class WebDriverWait(object): 10 def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None): 11 """Constructor, takes a WebDriver instance and timeout in seconds. 12 13 :Args: 14 - driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote) 15 - timeout - Number of seconds before timing out 16 - poll_frequency - sleep interval between calls 17 By default, it is 0.5 second. 18 - ignored_exceptions - iterable structure of exception classes ignored during calls. 19 By default, it contains NoSuchElementException only. 20 21 Example: 22 from selenium.webdriver.support.ui import WebDriverWait \n 23 element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId")) \n 24 is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).\ \n 25 until_not(lambda x: x.find_element_by_id("someId").is_displayed()) 26 """ 27 self._driver = driver 28 self._timeout = timeout 29 self._poll = poll_frequency 30 # avoid the divide by zero 31 if self._poll == 0: 32 self._poll = POLL_FREQUENCY 33 exceptions = list(IGNORED_EXCEPTIONS) 34 if ignored_exceptions is not None: 35 try: 36 exceptions.extend(iter(ignored_exceptions)) 37 except TypeError: # ignored_exceptions is not iterable 38 exceptions.append(ignored_exceptions) 39 self._ignored_exceptions = tuple(exceptions) 40 41 def until(self, method, message=''): 42 """Calls the method provided with the driver as an argument until the \ 43 return value is not False.""" 44 screen = None 45 stacktrace = None 46 47 end_time = time.time() + self._timeout 48 while True: 49 try: 50 value = method(self._driver) 51 if value: 52 return value 53 except self._ignored_exceptions as exc: 54 screen = getattr(exc, 'screen', None) 55 stacktrace = getattr(exc, 'stacktrace', None) 56 time.sleep(self._poll) 57 if time.time() > end_time: 58 break 59 raise TimeoutException(message, screen, stacktrace) 60 61 def until_not(self, method, message=''): 62 """Calls the method provided with the driver as an argument until the \ 63 return value is False.""" 64 end_time = time.time() + self._timeout 65 while True: 66 try: 67 value = method(self._driver) 68 if not value: 69 return value 70 except self._ignored_exceptions: 71 return True 72 time.sleep(self._poll) 73 if time.time() > end_time: 74 break 75 raise TimeoutException(message)
哈哈,我始終相信貼出來總會有人看。WebDriverWait 類位於selenium.webdriver.support.ui下面的例子很簡單,
Example:
1 from selenium.webdriver.support.ui import WebDriverWait 2 element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId")) 3 is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)). 4 until_not(lambda x: x.find_element_by_id("someId").is_displayed())
WebDriverWait 里面主要有兩個方法,一個是until和until_not
那么我們可以這樣用:
WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("某個按鈕")).click()
意思就是在10秒內等待某個按鈕被定位到,咱們再去點擊。就是每隔0.5秒內調用一下until里面的表達式或者方法函數,要么10秒內表達式執行成功,要么10秒后拋出超時異常。
然后我們再看expected_conditions:
1 from selenium.common.exceptions import NoSuchElementException 2 from selenium.common.exceptions import NoSuchFrameException 3 from selenium.common.exceptions import StaleElementReferenceException 4 from selenium.common.exceptions import WebDriverException 5 from selenium.common.exceptions import NoAlertPresentException 6 7 """ 8 * Canned "Expected Conditions" which are generally useful within webdriver 9 * tests. 10 """ 11 class title_is(object): 12 """An expectation for checking the title of a page. 13 title is the expected title, which must be an exact match 14 returns True if the title matches, false otherwise.""" 15 def __init__(self, title): 16 self.title = title 17 18 def __call__(self, driver): 19 return self.title == driver.title 20 21 class title_contains(object): 22 """ An expectation for checking that the title contains a case-sensitive 23 substring. title is the fragment of title expected 24 returns True when the title matches, False otherwise 25 """ 26 def __init__(self, title): 27 self.title = title 28 29 def __call__(self, driver): 30 return self.title in driver.title 31 32 class presence_of_element_located(object): 33 """ An expectation for checking that an element is present on the DOM 34 of a page. This does not necessarily mean that the element is visible. 35 locator - used to find the element 36 returns the WebElement once it is located 37 """ 38 def __init__(self, locator): 39 self.locator = locator 40 41 def __call__(self, driver): 42 return _find_element(driver, self.locator) 43 44 class visibility_of_element_located(object): 45 """ An expectation for checking that an element is present on the DOM of a 46 page and visible. Visibility means that the element is not only displayed 47 but also has a height and width that is greater than 0. 48 locator - used to find the element 49 returns the WebElement once it is located and visible 50 """ 51 def __init__(self, locator): 52 self.locator = locator 53 54 def __call__(self, driver): 55 try: 56 return _element_if_visible(_find_element(driver, self.locator)) 57 except StaleElementReferenceException: 58 return False 59 60 class visibility_of(object): 61 """ An expectation for checking that an element, known to be present on the 62 DOM of a page, is visible. Visibility means that the element is not only 63 displayed but also has a height and width that is greater than 0. 64 element is the WebElement 65 returns the (same) WebElement once it is visible 66 """ 67 def __init__(self, element): 68 self.element = element 69 70 def __call__(self, ignored): 71 return _element_if_visible(self.element) 72 73 def _element_if_visible(element, visibility=True): 74 return element if element.is_displayed() == visibility else False 75 76 class presence_of_all_elements_located(object): 77 """ An expectation for checking that there is at least one element present 78 on a web page. 79 locator is used to find the element 80 returns the list of WebElements once they are located 81 """ 82 def __init__(self, locator): 83 self.locator = locator 84 85 def __call__(self, driver): 86 return _find_elements(driver, self.locator) 87 88 class text_to_be_present_in_element(object): 89 """ An expectation for checking if the given text is present in the 90 specified element. 91 locator, text 92 """ 93 def __init__(self, locator, text_): 94 self.locator = locator 95 self.text = text_ 96 97 def __call__(self, driver): 98 try : 99 element_text = _find_element(driver, self.locator).text 100 return self.text in element_text 101 except StaleElementReferenceException: 102 return False 103 104 class text_to_be_present_in_element_value(object): 105 """ 106 An expectation for checking if the given text is present in the element's 107 locator, text 108 """ 109 def __init__(self, locator, text_): 110 self.locator = locator 111 self.text = text_ 112 113 def __call__(self, driver): 114 try: 115 element_text = _find_element(driver, 116 self.locator).get_attribute("value") 117 if element_text: 118 return self.text in element_text 119 else: 120 return False 121 except StaleElementReferenceException: 122 return False 123 124 class frame_to_be_available_and_switch_to_it(object): 125 """ An expectation for checking whether the given frame is available to 126 switch to. If the frame is available it switches the given driver to the 127 specified frame. 128 """ 129 def __init__(self, locator): 130 self.frame_locator = locator 131 132 def __call__(self, driver): 133 try: 134 if isinstance(self.frame_locator, tuple): 135 driver.switch_to.frame(_find_element(driver, 136 self.frame_locator)) 137 else: 138 driver.switch_to.frame(self.frame_locator) 139 return True 140 except NoSuchFrameException: 141 return False 142 143 class invisibility_of_element_located(object): 144 """ An Expectation for checking that an element is either invisible or not 145 present on the DOM. 146 147 locator used to find the element 148 """ 149 def __init__(self, locator): 150 self.locator = locator 151 152 def __call__(self, driver): 153 try: 154 return _element_if_visible(_find_element(driver, self.locator), False) 155 except (NoSuchElementException, StaleElementReferenceException): 156 # In the case of NoSuchElement, returns true because the element is 157 # not present in DOM. The try block checks if the element is present 158 # but is invisible. 159 # In the case of StaleElementReference, returns true because stale 160 # element reference implies that element is no longer visible. 161 return True 162 163 class element_to_be_clickable(object): 164 """ An Expectation for checking an element is visible and enabled such that 165 you can click it.""" 166 def __init__(self, locator): 167 self.locator = locator 168 169 def __call__(self, driver): 170 element = visibility_of_element_located(self.locator)(driver) 171 if element and element.is_enabled(): 172 return element 173 else: 174 return False 175 176 class staleness_of(object): 177 """ Wait until an element is no longer attached to the DOM. 178 element is the element to wait for. 179 returns False if the element is still attached to the DOM, true otherwise. 180 """ 181 def __init__(self, element): 182 self.element = element 183 184 def __call__(self, ignored): 185 try: 186 # Calling any method forces a staleness check 187 self.element.is_enabled() 188 return False 189 except StaleElementReferenceException as expected: 190 return True 191 192 class element_to_be_selected(object): 193 """ An expectation for checking the selection is selected. 194 element is WebElement object 195 """ 196 def __init__(self, element): 197 self.element = element 198 199 def __call__(self, ignored): 200 return self.element.is_selected() 201 202 class element_located_to_be_selected(object): 203 """An expectation for the element to be located is selected. 204 locator is a tuple of (by, path)""" 205 def __init__(self, locator): 206 self.locator = locator 207 208 def __call__(self, driver): 209 return _find_element(driver, self.locator).is_selected() 210 211 class element_selection_state_to_be(object): 212 """ An expectation for checking if the given element is selected. 213 element is WebElement object 214 is_selected is a Boolean." 215 """ 216 def __init__(self, element, is_selected): 217 self.element = element 218 self.is_selected = is_selected 219 220 def __call__(self, ignored): 221 return self.element.is_selected() == self.is_selected 222 223 class element_located_selection_state_to_be(object): 224 """ An expectation to locate an element and check if the selection state 225 specified is in that state. 226 locator is a tuple of (by, path) 227 is_selected is a boolean 228 """ 229 def __init__(self, locator, is_selected): 230 self.locator = locator 231 self.is_selected = is_selected 232 233 def __call__(self, driver): 234 try: 235 element = _find_element(driver, self.locator) 236 return element.is_selected() == self.is_selected 237 except StaleElementReferenceException: 238 return False 239 240 class alert_is_present(object): 241 """ Expect an alert to be present.""" 242 def __init__(self): 243 pass 244 245 def __call__(self, driver): 246 try: 247 alert = driver.switch_to.alert 248 alert.text 249 return alert 250 except NoAlertPresentException: 251 return False 252 253 def _find_element(driver, by): 254 """Looks up an element. Logs and re-raises ``WebDriverException`` 255 if thrown.""" 256 try : 257 return driver.find_element(*by) 258 except NoSuchElementException as e: 259 raise e 260 except WebDriverException as e: 261 raise e 262 263 def _find_elements(driver, by): 264 try : 265 return driver.find_elements(*by) 266 except WebDriverException as e: 267 raise e
哈哈,我相信這個py文件大家一看就能懂。這無非就是一些預期條件。
結合上面的WebDriverWait,我們可以這么用:
from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC ''' 10秒鍾等待瀏覽器彈出的對話框,如果出現,就點擊確定按鈕 ''' WebDriverWait(chromedriver,10).until(EC.alert_is_present()).accept()
我們的自動化腳本跑得快慢或者出現異常,很大程度上取決於我們設定的等待時間,如果你還是習慣於time.sleep(5)這種方式的話,這將會浪費很多時間。
根據expected_conditions.py文件的寫法,我們也可以定義一些自己的期待類,例如:
from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class must_get_url(object): ''' 必須到達的URL 參數: url - 必須到達的地址 ''' def __init__(self, url): self.url = url def __call__(self, driver): driver.get(self.url) return self.url == driver.current_url options = webdriver.ChromeOptions() options.add_argument("--test-type") chromedriver = webdriver.Chrome(r"E:\MyProject\WebDriver\chromedriver.exe",chrome_options=options) WebDriverWait(chromedriver,10).until(must_get_url("https://www.baidu.com"))
