在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自己的页面说明页面刷新时会出现此问题,但这是应用程序逻辑的必要部分,因为它已配置到目前为止。
答案 0 :(得分:2)
添加以下导入:
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)替换为更合理的&#34;
答案 1 :(得分:0)
我一直在使用硒一段时间,所以我理解了Stale Element Exception的挣扎。虽然不完美,但selenium提供了一系列"wait"命令,以允许网站加载完整。不幸的是,它不完美,因为每次运行加载可能需要不同的时间,但这些是硒提供的工具。
答案 2 :(得分:0)
我还没有在python中工作但是曾经在java / selenium上工作过。但是,我可以给你一个克服陈旧性的想法。
一般来说,如果在启动webelement之后更改了元素属性或某些内容,我们将获得Stale Exception。例如,在某些情况下,如果用户尝试在同一页面上单击同一元素但在页面刷新后,则会获取staleelement异常。
为了解决这个问题,我们可以创建新的webelement,以防页面被更改或刷新。下面的代码可以给你一些想法。(它在java中,但概念将是相同的)
示例:
webElement element = driver.findElement(by.xpath("//*[@id='StackOverflow']"));
element.click();
//page is refreshed
element.click();//This will obviously throw stale exception
为了解决这个问题,我们可以将xpath存储在某个字符串中,然后使用它创建一个新的webelement。
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);
}
希望这会对你有所帮助。感谢
答案 3 :(得分:0)
要防止元素过时,请在当前页面上放置一个新元素,点击链接并等待该元素不再可用。然后等待新页面上的元素出现
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 :(得分:0)
我和你一样读了同一本书并遇到了同样的问题(来自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)