I’ve been using Watir-WebDriver to create a a number of large suites of automated tests, and I’ve found that as others start to contribute tests, I am seeing the following exception happening quite frequently:
Selenium::WebDriver::Error::ObsoleteElementError: Element is no longer attached to the DOM
Now, it’s quite annoying when this happens, and it’s not consistent, but I will try to explain what is happening and how you can try to avoid it.
As Jari Bakken explains on the Watir mailing list, in vanilla Watir, when you define an element, the details of how to locate an element are stored, and the actual finding is done when you need to use the element.
This is dramatically different to Watir-WebDriver that stores an reference to the actual DOM element, so if this changes (the page refreshes or JavaScript updates the DOM for example) then the exception above is raised.
I’ve found there’s a couple of ways to reduce, but not entirely eliminate, the chance of these exceptions being raised. The main idea is to avoid looping over DOM elements, especially when the pages you are testing have a lot of heavy JavaScript.
Instead of looping over the elements like this:
browser.links.each do |link| link.click browser.goto home end
I have found it’s better to collect the hrefs of each, and use these collected values:
links = browser.links.collect { |link| link.href }
links.each do |link|
browser.goto link
browser.goto home
end
Instead of looping through a element collection to find some element value:
def error_exists? text
errors = browser.spans(:class => 'error').collect
errors.each do |span|
return true if span.text == text
end
false
end
I have found it is better to directly find the element itself
def error_exists? text browser.span(:class => 'error', :text => text).when_present.exists? end
Summary
The ObseleteElementError is a definitely a pain, but unless the entire approach to locating elements in Watir-Webdriver is changed, there is at least some way I have shown to minimize the pain.
It’s worth noting that watir-webdriver also stores the *how* initially – it’s only when the element is actually used that the DOM reference is cached in the Element object. Without lazily locating the element, things like .exist? and .when_present wouldn’t work.
Thanks a ton! This helped me a lot to get over this exception.