92

I am trying to automate some test cases using Java and Selenium WebDriver. I have the following scenario:

  • There is a page named 'Products'. When I click on 'View Details' link in the 'Product' page, a popup (modal-dialog) containing the details of the item appears.
  • When I click on the 'Close' button in the popup the popup closes and the page automatically refreshes (the page is just reloading, the contents remain unchanged).
  • After closing the popup I need to click on 'Add Item' button in the same page. But when WebDriver trying to find the 'Add Item' button, if the internet speed is too fast, WebDriver can find and click the element.

  • But if the internet is slow, WebDriver finds the button before the page refresh, but as soon as the WebDriver click on the button, the page refreshes and StaleElementReferenceException occurs.

  • Even if different waits are used, all the wait conditions become true (since the contents in the page are same before and after reload) even before the page is reloaded and StaleElementReferenceException occurs.

The test case works fine if Thread.sleep(3000); is used before clicking on the 'Add Item' button. Is there any other workaround for this problem?

Ratmir Asanov
  • 5,433
  • 5
  • 20
  • 36
stackoverflow
  • 1,710
  • 2
  • 15
  • 29
  • 2
    I use Selenium implicit wait, but it doesn't solve the problem – stackoverflow Apr 13 '16 at 10:58
  • This problem can easily be solved by using WebDriverWait which waits for a condition to return either true or non null value until a timeout occurs. The condition is, click on the element in a try-catch. In try, click on the element and return true if done, in catch, catch StaleElementReferenceException and return null. So, if clicking causes StaleElementException, the condition is executed again, until click is successfully done or timeout occurs. This will effectively wait for the reloading to complete. – p_champ Mar 02 '20 at 15:09
  • Something to add into the comment above: the element should also be retrieved in the try block before clicking, because once an element is redrawn, it's element identifier changes. – p_champ Mar 13 '20 at 11:29

5 Answers5

105

3 answers, which you can combine:

  1. Set implicit wait immediately after creating the web driver instance:

    _ = driver.Manage().Timeouts().ImplicitWait;

    This will try to wait until the page is fully loaded on every page navigation or page reload.

  2. After page navigation, call JavaScript return document.readyState until "complete" is returned. The web driver instance can serve as JavaScript executor. Sample code:

    C#

    new WebDriverWait(driver, MyDefaultTimeout).Until(
    d => ((IJavaScriptExecutor) d).ExecuteScript("return document.readyState").Equals("complete"));
    

    Java

    new WebDriverWait(firefoxDriver, pageLoadTimeout).until(
          webDriver -> ((JavascriptExecutor) webDriver).executeScript("return document.readyState").equals("complete"));
    
  3. Check if the URL matches the pattern you expect.

8protons
  • 2,802
  • 2
  • 23
  • 47
Kim Homann
  • 2,444
  • 1
  • 12
  • 19
  • 2
    driver.manage().timeouts().implicitlyWait() in java asking for 2 arguments (long arg0, TimeUnit arg1) which is a timeout or what? – Nasif Imtiaz Ohi May 01 '18 at 18:34
  • 3
    and I believe python is (feel free to add this to your answer): selenium.webdriver.support.ui.WebDriverWait(self.driver, 10).until(lambda d: d.execute_script("return document.readyState") == "complete") – Jack Davidson Jan 30 '19 at 17:44
17

It seems that you need to wait for the page to be reloaded before clicking on the "Add" button. In this case you could wait for the "Add Item" element to become stale before clicking on the reloaded element:

WebDriverWait wait = new WebDriverWait(driver, 20);
By addItem = By.xpath("//input[.='Add Item']");

// get the "Add Item" element
WebElement element = wait.until(ExpectedConditions.presenceOfElementLocated(addItem));

//trigger the reaload of the page
driver.findElement(By.id("...")).click();

// wait the element "Add Item" to become stale
wait.until(ExpectedConditions.stalenessOf(element));

// click on "Add Item" once the page is reloaded
wait.until(ExpectedConditions.presenceOfElementLocated(addItem)).click();
Florent B.
  • 37,063
  • 6
  • 68
  • 92
12

You can do this in many ways before clicking on add items:

WebDriverWait wait = new WebDriverWait(driver, 40);
wait.until(ExpectedConditions.elementToBeClickable(By.id("urelementid"))); // instead of id you can use cssSelector or xpath of your element.

or:

wait.until(ExpectedConditions.visibilityOfElementLocated("urelement"));

You can also wait like this. If you want to wait until invisible of previous page element:

wait.until(ExpectedConditions.invisibilityOfElementLocated("urelement"));

Here is the link where you can find all the Selenium WebDriver APIs that can be used for wait and its documentation.

nik7
  • 747
  • 2
  • 8
  • 17
noor
  • 2,746
  • 2
  • 14
  • 26
-1

yes stale element error is thrown when (taking your scenario) you have defined locator strategy to click on 'Add Item' first and then when you close the pop up the page gets refreshed hence the reference defined for 'Add Item' is lost in the memory so to overcome this you have to redefine the locator strategy for 'Add Item' again

understand it with a dummy code

// clicking on view details 
driver.findElement(By.id("")).click();
// closing the pop up 
driver.findElement(By.id("")).click();


// and when you try to click on Add Item
driver.findElement(By.id("")).click();
// you get stale element exception as reference to add item is lost 
// so to overcome this you have to re identify the locator strategy for add item 
// Please note : this is one of the way to overcome stale element exception 

// Step 1 please add a universal wait in your script like below 
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS); // just after you have initiated browser
Community
  • 1
  • 1
eduliant
  • 2,514
  • 4
  • 17
  • 32
-6

There are two different ways to use delay in selenium one which is most commonly in use. Please try this:

driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);

second one which you can use that is simply try catch method by using that method you can get your desire result.if you want example code feel free to contact me defiantly I will provide related code

Pramod Karandikar
  • 4,949
  • 6
  • 37
  • 60