Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid StaleElementReferenceException in Selenium - Python

I am stuck in writing a Python Selenium script and can't seem to satisfactorily resolve this StaleElementReferenceException I am getting.

I have my page loaded and click a button which opens a form that allows the user to add a new credit card to the order. At this point I do a WebDriverWait to pause the script until the Save button on this form becomes visible. At that point, recreate the page object since it has changed, and my intent is to populate the fields and save the card.

The problem is that after refreshing the page object the script fails with the StaleElementReferenceException. My understanding is that the WebDriverWait will pause the the execution giving the page time to load all the elements that need to load, but that doesn't appear to be happening. Instead something in that refresh of the page object is stale and causes the error (different part of the object creation each time).

If I just uncomment the line 'time.sleep(2)' then this script runs fine and it will pass. So I know I just need to give the page time to reload correctly before I refresh the object. The WebDriverWait just doesn't seem to be doing that effectively for me.

Is there a more correct way I can do this without the sleep command?

checkout = CheckoutProcess(self.driver)

# Add Credit Card
checkout.add_credit_card()

# Wait for form to display
WebDriverWait(self.driver,30).until(
    expected_conditions.presence_of_element_located((By.CLASS_NAME, 'total')))

# time.sleep(2)

# Refresh the page object so form can be filled in
checkout = CheckoutProcess(self.driver)  # Script Fails Here
checkout.populate_credit_card_data(
    credit_card_name, credit_card_number,
    credit_card_expiration_date, credit_card_cvc)
checkout.click_credit_card_save_button()
like image 274
Pachilds Avatar asked Oct 13 '16 19:10

Pachilds


2 Answers

A StaleElementReferenceException is thrown when the element you were interacting is destroyed and then recreated. Most complex web pages these days will move things about on the fly as the user interacts with it and this requires elements in the DOM to be destroyed and recreated.

Try doing

wait.until(ExpectedConditions.stalenessOf(whatever element));

or

wait.until(ExpectedConditions.presenceOfElementLocated(By.id("whatever elemnt")))

Hope it will help you

like image 57
Anuraj R Avatar answered Nov 14 '22 22:11

Anuraj R


I had this same problem and solved it by implementing this wrapper that retries when the exception is caught:

from selenium.common.exceptions import StaleElementReferenceException

def _loop_is_text_present(text, max_attempts=3):
    attempt = 1
    while True:
        try:
            return self.browser.is_text_present(text)
        except StaleElementReferenceException:
            if attempt == max_attempts:
                raise
            attempt += 1

Inspired by: http://darrellgrainger.blogspot.com/2012/06/staleelementexception.html

like image 28
Rob Bednark Avatar answered Nov 14 '22 21:11

Rob Bednark