Selenium with Python: Stale Element Exception Exception

Works from Test Driven Development with Python and I am currently facing "StaleElementReferenceException" when running a functional test right after migration. Here's the full text of the error:

ERROR: test_start_and_retrieve_list (__main__.NewVisitorTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "functional_tests.py", line 53, in test_start_and_retrieve_list
    rows = table.find_elements_by_tag_name('tr')
  File "/usr/local/lib/python3.5/dist-packages/selenium/webdriver/remote/webelement.py", line 237, in find_elements_by_tag_name
    return self.find_elements(by=By.TAG_NAME, value=name)
  File "/usr/local/lib/python3.5/dist-packages/selenium/webdriver/remote/webelement.py", line 527, in find_elements
    {"using": by, "value": value})['value']
  File "/usr/local/lib/python3.5/dist-packages/selenium/webdriver/remote/webelement.py", line 493, in _execute
    return self._parent.execute(command, params)
  File "/usr/local/lib/python3.5/dist-packages/selenium/webdriver/remote/webdriver.py", line 256, in execute
    self.error_handler.check_response(response)
  File "/usr/local/lib/python3.5/dist-packages/selenium/webdriver/remote/errorhandler.py", line 194, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.StaleElementReferenceException: Message: The element reference of <table id="id_list_table"> stale: either the element is no longer attached to the DOM or the page has been refreshed


----------------------------------------------------------------------
Ran 1 test in 8.735s

FAILED (errors=1)

      

Here's the test:

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import unittest

class NewVisitorTest(unittest.TestCase): 

    def setUp(self):
        self.browser = webdriver.Firefox()
        self.browser.implicitly_wait(3)

    def tearDown(self):
        self.browser.close()

    def check_for_row(self, row_text):
        table = self.browser.find_element_by_id('id_list_table')
        rows = table.find_elements_by_tag_name('tr')
        self.assertIn(row_text, [row.text for row in rows])

    def test_start_and_retrieve_list(self):    
        self.browser.get('http://localhost:8000')

        self.assertIn('To-Do', self.browser.title)
        header_text = self.browser.find_element_by_tag_name('h1').text
        self.assertIn('To-Do', header_text)

        inputbox = self.browser.find_element_by_id('id_new_item')
        self.assertEqual(
            inputbox.get_attribute('placeholder'),
            'Enter a to-do item'
        )

        inputbox.send_keys('Buy peacock feathers')

        inputbox.send_keys(Keys.ENTER)
        self.check_for_row('1: Buy peacock feathers')

        inputbox = self.browser.find_element_by_id('id_new_item')
        inputbox.send_keys('Use peacock feathers to make a fly')
        inputbox.send_keys(Keys.ENTER)

        table = self.browser.find_element_by_id('id_list_table')
        rows = table.find_elements_by_tag_name('tr')
        self.check_for_row('1: Buy peacock feathers')
        self.check_for_row('2: Use peacock feathers to make a fly')

        self.fail('Finish the test!')

if __name__ == '__main__':
    unittest.main(warnings='ignore')

      

How do I set up a test to prevent this? Selenium's own page says that this problem can occur when the page is refreshed, but this is a necessary part of the application logic as it is configured so far.

+3


source to share


5 answers


I have been using selenium for a while, so I understand the difficulty of Excluding Style Element. While not ideal, selenium provides a series of "wait" commands to load the entire site. Unfortunately, its less than ideal loading may take different times on each run, but these are the tools selenium provides.



0


source


I have not worked in python but have worked on java / selenium. But, I can give you an idea to overcome the stagnation.

Typically, we will get a Stale Exception if the element's attributes or something changes after webelement runs. For example, in some cases, if a user tries to click the same element on the same page, but after refreshing the page, he gets a staleelement.

To overcome this, we can create a new web element in case the page has been changed or updated. Below code might give you some idea. (It's in java, but the concept would be the same)

Example:

webElement element = driver.findElement(by.xpath("//*[@id='StackOverflow']"));
element.click();
//page is refreshed
element.click();//This will obviously throw stale exception

      



To overcome this, we can store the xpath on some string and use it to create a new web element as we go.

String xpath = "//*[@id='StackOverflow']";
driver.findElement(by.xpath(xpath)).click();
//page has been refreshed. Now create a new element and work on it
driver.fineElement(by.xpath(xpath)).click();   //This works

      

Another example:

for(int i = 0; i<5; i++)
{
  String value = driver.findElement(by.xpath("//.....["+i+"]")).getText);
  System.out.println(value);
} 

      

Hope this helps you. Thanks to

0


source


To prevent an item from becoming obsolete, place a new item on the current page, click on the link, and wait until the item is no longer available. Then wait for the item to appear on the new page

    script_to_execute = """
            var new_element = document.createElement('span');
            new_element.setAttribute('id', 'wait4me');
            document.body.appendChild(new_element);"""
    self.driver.execute_script(script_to_execute)
    self.driver.find_element_by_xpath("{}".format(locator)).click()
    WebDriverWait(self.driver, self.time_out).until (
                lambda x: not x.find_elements_by_id("wait4me")) 

      

This issue occurs when the loop starts before the refreshed page is fully loaded. Especially when you refresh the page in an application or form. One way is to place the item on the current page, then update and use the WebDriverWait statement until the item is found. Then start your loop. (Otherwise, a reboot occurs during a loop ...)

0


source


I read the same book as you and ran into the same problem (the solutions from this page didn't work for me). This is how I resolved it.

Problem

An exception is thrown whenever you try to access an obsolete object. Therefore, we need to wait for a situation where this exception is NOT thrown anymore.

My decision

I created a method that waits for my actions until they pass

from selenium.common.exceptions import StaleElementReferenceException
[...]
def stale_aware_for_action(self, action):
    while(True):
        try:
            action()
            break
        except StaleElementReferenceException:
            continue

      

And in the test method, I have defined the actions that I want to wait to complete:

def test_can_start_a_list_and_retrieve_it_later(self):
    [...]
    def insert_second_item_to_inputbox():
        inputbox = self.browser.find_element_by_id('id_new_item')
        inputbox.send_keys('Use peacock feathers to make a fly')
        inputbox.send_keys(Keys.ENTER)

    self.stale_aware_for_action(insert_second_item_to_inputbox)

    def check_for_first_item():
        self.check_for_row_in_list_table('1: Buy peacock feathers')
    def check_for_second_item():
        self.check_for_row_in_list_table('2: Use peacock feathers to make a fly')

    self.stale_aware_for_action(check_for_first_item)
    self.stale_aware_for_action(check_for_second_item)

      

0


source


Add these imports:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions

      

Change these lines

inputbox.send_keys(Keys.ENTER)
self.check_for_row('1: Buy peacock feathers')

      

in

inputbox.send_keys(Keys.ENTER)

WebDriverWait(self.browser, 10).until(
            expected_conditions.text_to_be_present_in_element(
                (By.ID, 'id_list_table'), 'Buy peacock feathers'))

self.check_for_row('1: Buy peacock feathers')

      

This replaces time.sleep (1) with something more "sensible"

0


source







All Articles