【问题标题】:Selenium with Python: Stale Element Reference ExceptionSelenium 与 Python:过时的元素引用异常
【发布时间】:2017-12-24 00:46:15
【问题描述】:

Test Driven Development with Python 工作,我目前在迁移后立即运行功能测试时遇到“StaleElementReferenceException”。以下是错误的全文:

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)

这是测试:

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')

如何配置测试以防止这种情况发生? Selenium 自己的页面说,当页面刷新时可能会发生此问题,但这是应用程序逻辑的必要部分,因为它是迄今为止配置的。

【问题讨论】:

  • 我在这里写了一个完整的答案:stackoverflow.com/questions/41737974/…
  • 我遇到了同样的问题 - 为了查找行,我们可以使用 self.browser.find_elements_by_tag_name('tr') 而不是 table.find_elements_by_tag_name('tr')。换句话说,我们每次尝试查找元素时都可以使用浏览器,而不是使用一个元素来查找其他元素。

标签: python selenium automation


【解决方案1】:

添加这些导入:

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

更改这些行

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

到:

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')

这将 time.sleep(1) 替换为更“合理”的东西

【讨论】:

    【解决方案2】:

    我和你读了同一本书,遇到了同样的问题(this page 的解决方案对我不起作用)。 以下是我的解决方法。

    问题

    每当您尝试访问陈旧的对象时都会引发异常。所以我们必须等待不再抛出这个异常的情况。

    我的解决方案

    我创建了等待我的操作通过的方法

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

    在测试方法中,我定义了要等待完成的操作:

    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)
    

    【讨论】:

      【解决方案3】:

      为防止元素过时,请在当前页面上放置一个新元素,点击链接并等待该元素不再可用。然后等待新页面上的一个元素出现

          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")) 
      

      当循环在更新的页面完全加载之前开始时会发生此问题。特别是当您更新应用程序或表单中的页面时。 一种解决方法是在当前页面上放置一个元素,然后更新并使用 WebDriverWait 语句,直到不再找到该元素。 然后开始你的循环。 (否则在循环期间会发生重新加载...)

      【讨论】:

        【解决方案4】:

        我没有在 python 中工作过,但在 java/selenium 上工作过。但是,我可以给你一个克服陈旧的想法。

        通常,如果在启动 web 元素后元素属性或某些内容发生更改,我们将收到 Stale Exception。例如,在某些情况下,如果用户尝试单击同一页面上的同一元素,但在页面刷新后,会出现 staleelement 异常。

        为了克服这个问题,我们可以创建新的 web 元素,以防页面发生更改或刷新。下面的代码可以给你一些想法。(它在java中,但概念是一样的)

        例子:

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

        为了克服这个问题,我们可以将 xpath 存储在一些字符串中,并使用它创建一个新的 web 元素。

        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
        

        另一个例子:

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

        希望这对您有所帮助。谢谢

        【讨论】:

        • 桑托什,谢谢你的建议。但是,在实施此 xpath 解决方案后,我收到与以前相同的错误,即我引用的元素已过时。
        • 好的...这是我所知道的陈旧性的通用解决方案。如果您不介意,您是否可以再次检查更新的代码并确认您仅在页面稳定后才创建了 webelement(例如刷新后/不使用之前创建的 webelement)。
        【解决方案5】:

        我已经使用 selenium 有一段时间了,所以我了解 Stale Element Exception 的困难。虽然并不完美,但 selenium 提供了一系列 "wait" 命令以允许网站加载完成。不幸的是,它并不完美,因为每次运行加载可能需要不同的时间,但这些是 selenium 提供的工具。

        【讨论】:

          猜你喜欢
          • 2018-04-01
          • 2020-08-04
          • 2018-04-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-09
          • 1970-01-01
          相关资源
          最近更新 更多