I'm using Robot Framework and Selenium to test a website that has a language selector. I need to be able to select a language and then verify the page has actually changed to that language.
Since after choosing a new language the value in the lang
attribute of the <html>
tag changes, I decided I would use that to validate the language has been successfully changed. But I'm getting flaky results, as my test sometimes passes and sometimes doesn't.
This is the definition of the keyword I'm using:
CHANGE LANGUAGE
[Arguments] ${lang}
Select From List By Value ${LANGUAGE SWITCH} ?hl=${lang}
Wait Until Page Contains Element css=html
${doc lang} Get Element Attribute css=html@lang
Should Be True '${doc lang}'=='${lang}' timeout=15s
Since I have to execute this keyword quite a few times (one per each available language), I keep getting pretty often the dreaded "stale element" error: | FAIL | stale element reference: element is not attached to the page document
.
I read through this article and a few other questions in here and understand this could happen if an element reference is updated after obtaining it. However, I'm not sure how exactly I should modify my keyword to avoid getting this error.
The stale element reference error is a WebDriver error that occurs because the referenced web element is no longer attached to the DOM. Every DOM element is represented in WebDriver by a unique identifying reference, known as a web element.
It means the element is no longer in the DOM, or it changed. The following code will help you find the element by controlling and ignoring StaleElementExceptions and handling them just like any other NoSuchElementException. It waits for the element to NOT be stale, just like it waits for the element to be present.
Using the information that everyone has so kindly provided, I may have found a potential fix (not sure if it's robust enough not to throw the exception anymore, but after a few test runs they have all passed): I added a "Wait Until Keyword Succeeds" and moved the language validation to a new keyword:
VALIDATE PAGE LANGUAGE
[Arguments] ${lang}
${doc lang} Get Element Attribute css=html@lang
Should Be True '${doc lang}'=='${lang}'
CHANGE LANGUAGE
[Arguments] ${lang}
Select From List By Value ${LANGUAGE SWITCH} ?hl=${lang}
Wait For Condition return document.readyState=="complete"
Wait Until Keyword Succeeds 5 5s VALIDATE PAGE LANGUAGE ${lang}
Then I call this "CHANGE LANGUAGE" keyword as many times as languages I need to test.
I added this as an answer instead of a comment so I could show the code in a more readable way.
In order to wait for a page to be ready to test after a user action (clicking a link or button, for example), the algorithm I've found that seems to be almost bulletproof is this:
document.readyState
to be "complete"
Step 4 may not be necessary, but it doesn't hurt.
This has worked extremely well for my team. This can still fail since you might have some async javascript that runs after document.readyState
has been set, but there's simply no generic solution to that.
If your pages have a bunch of async javascript, you'll have to come up with your own scheme to know when the page is finally ready to be tested. For example, the last job to complete could set a flag, or you could wait until there are no pending async jobs, etc.
I'm not sure if you can do the above with robot keywords since it relies on the selenium staleness_of
condition. It's pretty easy to implement in python, though.
The inspiration for this solution came from this blog post: How to get Selenium to wait for page load after a click
If you use my page object library for robot, this is built-in as a context manager.
As highlighted, a Stale Element
error typically means that between the element retrieval and the corresponding action the element changed. Often this is due to a page refresh.
For this reason it is important to invest in a robust waiting approach. Not guessing that your page/application has completed loading, but instead knowing it has completed. This will not only prevent Stale Element
errors but also speed up your tests as you're not unnecessarily waiting.
As the Get Element Attribute ${DOCUMENT}@lang
is causing the stale element error and the Select From List By Value ${LANGUAGE SWITCH} ?hl=${lang}
is causing the page refresh, then that leaves the Wait Until Page Contains Element html
as your waiting approach.
As the <html>
tag is always present and the first to be loaded in the DOM, this is not the best tag to wait for. I'd advise something unique for the loaded page or the last element of that page. Though I have to stress that this still constitutes to guessing the page has loaded.
It is better to invest in a robust waiting approach. Especially if your application is using a framework like Angular, React or jQuery then you have several Javascript markers to help you with this. For some frameworks there are even custom Robot Framework Libraries that support their specific markers.
Should your application not use a framework, then talk to your developers and have them develop this for you. Simplest would be a visible spinner, but a Javascript function that returns True
will work just as well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With