Retrying on Exceptions Python Selenium
Dec. 18, 2018
There are many times when running selenium test where you think "if only the method just ran again when that happened". Trying to make selenium test to handle every use case might not be worth your time, or even practical. For example, I kept getting an error for an element not being clickable simply because notifications popped up in rare cases.
My favorite solution I have found for such cases is to simply run the code again. A very clean way I have found to do just that is to decorate my methods in page objects which I know are likely to fail. For most applications, I'd say this means the code should be revised instead of being allowed to fail; however, selenium test are a fickle mistress, and rerunning the code in rare cases is the correct thing to do.
For example, say for the following page object, my search_field
method often failed when I attempted to access the field to type a value into due to NoSuchElementException
and StaleElementReferenceException
.
class GenericPromptPage(object): def __init__(self, driver): self.driver = driver @property def search_field(self): return self.driver.find_element_by_css_selector('.my-class .another-class input')
My decorated page object looks like this.
class GenericPromptPage(object): def __init__(self, driver): self.driver = driver @property @retry_on_no_element @retry_on_stale_element def search_field(self): return self.driver.find_element_by_css_selector('.my-class .another-class input')
With this new page object, if search_field
fails, then the method will rerun itself! It is very clean, readable, and scalable throughout the code base. To actually create the decorators, I created a decorator factory since they all held the same form. It ended up looking like this.
from time import sleep from functools import wraps from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException def retry_on_exception_factory(exc): def retry_on_exc(f): @wraps(f) def wrapped_f(*args, **kwargs): try: return f(*args, **kwargs) except exc: sleep(5) return f(*args, **kwargs) return wrapped_f return retry_on_exc retry_on_no_element = retry_on_exception_factory(NoSuchElementException) retry_on_stale_element = retry_on_exception_factory(StaleElementReferenceException)
Happy Coding!