Robot Framework+Selenium: how to avoid "stale element" error (flaky test)? - selenium

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.

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:
get a reference to the html element
perform the action that will cause the page to change (eg: click a link or button)
wait for the html element to go stale - this signals that the refresh has started
wait for 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.

I have created a custom keyword when ever i want to click an element or perform and action i would call this custom keyword . This custom keyword uses the built-in keyword 'Wait Until Keyword Succeeds' which runs the specified keyword and retries if it fails. through this built-in keyword the number of retry's and the time to wait before trying to run the keyword again after the previous run has failed can be configured.
This custom keyword would call another custom keyword where three SeleniumLibrary keywords would be called. The first one would Waits until element locator is enabled keyword, which would wait until the element is not disabled nor read-only. Once the element is enabled then focus would be taken to the element where we are going to perform action. Finally perform the action. All these precautions has prevented me from Stale Element error.
HighLevelKeyword_Identify And Click Element
[Arguments] ${locator}
Wait Until Keyword Succeeds ${RETRY_ATTEMPTS} ${RETRY_AFTER} Identify And Click Element ${locator}
Identify And Click Element
[Arguments] ${locator}
Wait Until Element Is Enabled ${locator}
Set Focus To Element ${locator}
Click Element ${locator}

Related

Selenium tests fail with azure pipeline sometimes and pass locally

Test cases fail while not finding a link or any validation messages after a button click.
I used explicit waits for page to load:
var waitForDocumentReady = new WebDriverWait(WebDriver, TimeSpan.FromSeconds(10));
waitForDocumentReady.Until((wdriver) => (WebDriver as IJavaScriptExecutor).ExecuteScript("return document.readyState").Equals("complete"));
For wait specific div of validation messages:
WebDriverWait wait = new WebDriverWait(WebDriver, TimeSpan.FromSeconds(10));
wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementExists(By.ClassName("validationErrors")));
But test cases pass sometimes with pipeline and fails sometimes.
I would recommend following:
When ever your tests fail take a snapshot at that moment.
sometimes when you run your tests locally the network communication
is a bit faster than running on server. Which means a spinner or a
loading control may not appear locally because results were retrieved
fast enough but the loader will appear when there is delay from
server in sending the response to client.
If you are trying to run your tests in FF headless on remote grid
setup then it is not yet stable in my experience.
Identify the pattern in failed scenarios which frequently fail and
try and run them multiple times in a loop and identify the pattern of
failure. In one of my case there was an actual bug in the performance
of the application which used to get reproduced whenever the tests
ran immediately in bulk
I was having this issue, except my test would always fail in Azure Pipelines but pass locally. Not running in "headless" mode fixed my problem. To do this, make sure "Test mix contains UI tests" is checked in your "Visual Studio Test" task.
There are a couple of things you need to take care as follows:
Test cases fail while not finding a link or any validation messages after a button click: A lot depends on the attributes of the button. In case the button contains either of the following attributes/events the DOM Tree would be getting updated and subsequently trying to find the desired element may fail:
onclick() event:
<button onclick="myFunction()">Click me</button>
jQuery click() Event:
$("p").click(function(){
alert("The paragraph was clicked.");
});
So after a button click, to find the desired element you need to induce you have to induce WebDriverWait for the ElementExists().
You can find a relevant discussion in ExpectedConditions.ElementIsVisible returns TimeoutException even when element is present
ExecuteScript("return document.readyState").Equals("complete"): This line of code is not that effective to ensure if the desired element(s) are present/visible/interactable with in the HTML DOM.
You can find a relevant discussion in Do we have any generic function to check if page has completely loaded in Selenium
However to wait for specific div with ElementExists() should have worked. The relevant HTML would have helped us to analyze what went wrong. However, if the validationErrors related element is based on HTML5 Constraint validation then you have to use the GetAttribute() method.
You can find a relevant discussion in How to handle html5 constraint validation pop-up using Selenium?

Robot Framework - Locating input element with accept attribute fails

I am writing an automation script for an avatar upload module with the following CSS locator:
input[accept="image/png,image/jpeg,image/gif,image/bmp"]
I am using Robot Framework's Wait Until Element Is Visible keyword to look for the locator above but is unsuccessful with the error:
Element 'css=input[accept="image/png,image/jpeg,image/gif,image/bmp"]' not visible after 30 seconds.
Increasing the timeout also doesn't work. Using the same in Chrome Dev Tools would successfully find the element. My guess is that the commas/slashes are messing with Robot's locator parsing. My question is: What is the correct way to write the locator?
Though present in the DOM, an element may not be visible/rendered. This is very often the case with file upload input elements - the UI renders something different, a button, div that had applied styling and fits in better with the overall design.
Thus a check is it visible will rightfully fail. Change your pre-usage approach to validate the input is in the HTML - this is actually the same as what you did in the browser's dev tools - with the Page Should Contain Element keyword, and proceed on success.
There is no problem with the CSS locator your are using. Maybe the element is in another iframe?

Randomly get "Element is no longer attached to the DOM" while running robot framework test

I have a test written in Robot Framework which runs over my application's Web-based User Interface. The problem is there are random times that the script can't access web elements and raise this error:
StaleElementReferenceException: Message: Element is no longer attached to the DOM
Stacktrace:
at fxdriver.cache.getElementAt (resource://fxdriver/modules/web-element-cache.js:8984)
at Utils.getElementAt (file:///c:/users/user/appdata/local/temp/tmp4xd_ww/webdriver-py-profilecopy/extensions/fxdriver#googlecode.com/components/command-processor.js:8574)
at WebElement.getElementAttribute (file:///c:/users/user/appdata/local/temp/tmp4xd_ww/webdriver-py-profilecopy/extensions/fxdriver#googlecode.com/components/command-processor.js:11764)
at DelayedCommand.prototype.executeInternal_/h (file:///c:/users/user/appdata/local/temp/tmp4xd_ww/webdriver-py-profilecopy/extensions/fxdriver#googlecode.com/components/command-processor.js:12282)
at DelayedCommand.prototype.executeInternal_ (file:///c:/users/user/appdata/local/temp/tmp4xd_ww/webdriver-py-profilecopy/extensions/fxdriver#googlecode.com/components/command-processor.js:12287)
at DelayedCommand.prototype.execute/< (file:///c:/users/user/appdata/local/temp/tmp4xd_ww/webdriver-py-profilecopy/extensions/fxdriver#googlecode.com/components/command-processor.js:12229)
I googled and I found out that it's related to my waits. Some times in my code, I wait for an element to be visible and then click on it:
wait until element is visible xpath=//*[#id="UserTableForm:${defRow}:0"]/input 2
click element xpath=//*[#id="UserTableForm:${defRow}:0"]/input
I think some times the DOM actually changes the web elements between these two commands; but I don't know how to avoid that?

What to do when waiting for an element is not enough?

I am writing selenium test scripts using the industry standard of webdriver waits before interacting with elements, but I still frequently find my tests are failing, and it seems to be due to a race condition.
Here's the example I have been running into lately:
Go to the product catalog page
Apply a filter
Wait for the filter to be applied
Click the save button on the product which loads after the filter is applied
Step number 4 only works if I place a Thread.Sleep() in front of the step - using webdriverwait is not enough. I'm guessing this is because the webdriverwait only waits until the element is attached to the DOM, even though the relevant JavaScript click event has not been added to the element.
How do you get around this issue? Is there an industry standard for dealing with this race condition?
EDIT This was resolved by upgrading to the latest version firefox. Thanks everyone!
As we discovered in comments, updating Firefox to the latest version did the trick.
The code looks really good to me and makes total sense.
What I would try is to move to the element before making a click:
Actions builder = new Actions(WebDriver);
IWebElement saveButton = wait.Until(ExpectedConditions.ElementIsVisible(By.CssSelector(".button-wishlist")));
Actions hoverClick = builder.MoveToElement(saveButton).Click();
hoverClick.Build().Perform();
As we've discovered in comments, the issue is related to the size of the window (the test passed without a Thread.sleep() if the browser window is maximized). This makes me think that if you scroll to the element before making a click it could be enough to make it work:
IWebElement saveButton = wait.Until(ExpectedConditions.ElementIsVisible(By.CssSelector(".button-wishlist")));
((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].scrollIntoView(true);", saveButton);
Actions hoverClick = builder.MoveToElement(saveButton).Click();
hoverClick.Build().Perform();
Take a look at this SO post for custom wait method. Sounds like element presence is not enough of a check in your case because the button may be present at all times in the DOM. What you need is something along the lines of ExpectedConditions.elementToBeClickable().
I am not familiar with the C# API but it looks like there is no built in method to do the same thing as in Java. So you could write a custom wait function that will have checks according to your needs.

Need help on selenium hidden elements

I am trying to use selenium to automatically log in the following website:
here is the link https://www.theice.com/
After I click on the log in, a log in screen comes up - I don't know what this is: frame,panel ...
I have not been successful in detecting what elements,properties this screen has through selenium. Thus I am not able to fill in the necessary info through selenium.
Can anyone shed some light on this?
SiKing is correct, here. Nothing too fancy going on with the DOM. Use your favorite DOM inspector and examine the elements and you should be able to identify the locators.
In this case, it looks like the front end devs were thorough and gave major elements 'id' tags. Once you can establish a relationship from an element to it's id, you'll just need to code the selenium commands to interact with them.
While there are a number of good tools for this, I like to recommend FireBug and FirePath addons for Firefox. Similarly to Chrome's inspector, hovering over a DOM element in the inspector will highlight the element itself on the page. FirePath can assist with CSS selectors and XPath if need be.
Without knowing your method of development (code by hand or SeleniumIDE) or you chosen language, I'd simply recommend reading the Selenium docs over at http://docs.seleniumhq.org/docs/03_webdriver.jsp#selenium-webdriver-api-commands-and-operations to get a feel for the means by which elements can be identified and subsequently used.
Incidentally, this site is an example of a good front end design (at least from a cursory glance). Locating elements, once you're familiar with the concepts, should be relatively simple. Play around with it, the best way to learn is by doing (that's why I haven't just given you the solution), you'll get it.
Best of luck.
(Also, I've not meant this message as 'talking down' to you. I'm not familiar with your level of understanding here, so I'm just starting with the basics).
Edit:
It occurred to me, after writing my initial response, that you may encounter an issue with the modal being displayed. After clicking the 'log in' link, you may want to add a wait to your test to ensure enough time for the modal to appear before interacting with it's elements. A simple Thread.sleep() could work here, but I hate recommending that unless it's a last resort situation (we all know sleeps suck, but sometimes it just needs to be done)... Instead, read up on the WebDriverWait class (assuming java) and the ExpectedConditions class. You could pass a locator for the root modal element, or a child thereof into wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("yourLocator")); which will wait until the condition is satisfied (or a timeout occurs) rather than the Thread.sleep() making a hard pause and possibly waiting for a needlessly long amount of time.
The point is, since the modal display isn't instantaneous, WebDriver may choke if it tries to interact with an element that hasn't yet appeared. Using a wait will relieve this problem.