MagicBeans
MagicBeans

Reputation: 373

Thread.sleep works but implicit wait, webdriverwait and fluent wait does not?

driver.findElement(By.xpath(sOptionPath)).click();  //this option button changes contents of page   
Thread.sleep(4000);
WebElement combo=driver.findElement(By.xpath(sXpath));
Select dropdownvalue = new Select(combo);
        dropdownvalue.selectByVisibleText(sText);

This above code works fine but if I use wait instead of thread.sleep I get StaleElementReferenceException exception. This is the Fluent wait I used :

    Wait<WebDriver> newwait=new FluentWait<WebDriver>(driver).withTimeout(10, TimeUnit.SECONDS).pollingEvery(1, TimeUnit.SECONDS).ignoring(StaleElementReferenceException.class);

        WebElement combo=newwait.until(new ExpectedCondition<WebElement>(){
            @Override
            public WebElement apply(WebDriver driver) {
                return driver.findElement(By.xpath(sXpath));
            }

        });

this finds the combobox, but performing any operation on combobox again gives NoSuchElement or statestate exception. SO I also tried this to select value from combobox :

    Wait<WebElement> elwait=new FluentWait<WebElement>(combo).withTimeout(10, TimeUnit.SECONDS).pollingEvery(1, TimeUnit.SECONDS).ignoring(StaleElementReferenceException.class,NoSuchElementException.class);

        Boolean a=elwait.until(new Function<WebElement,Boolean>(){
            @Override
            public Boolean apply(WebElement arg0) {
                Select dropdownvalue = new Select(arg0);
                dropdownvalue.selectByVisibleText(sText);
                return true;
            }

        });

This timeouts and doesnot work !

How can I make this work and why is it not working and thread.sleep working. And why using Thread.sleep a bad practice ?

Upvotes: 1

Views: 2734

Answers (2)

Jeremiah
Jeremiah

Reputation: 1145

I would try to validate that the object I'm returning to 'combo' is not stale using some of the existing checks from ExpectedCondtions class.

   Wait<WebDriver> newwait=new FluentWait<WebDriver>(driver).withTimeout(10, TimeUnit.SECONDS).pollingEvery(1, TimeUnit.SECONDS).ignoring(StaleElementReferenceException.class);

        WebElement combo=newwait.until(new ExpectedCondition<WebElement>(){
            @Override
            public WebElement apply(WebDriver driver) {
                WebElement found = driver.findElement(By.xpath(sXpath));
                if (ExpectedConditions.stalenessOf(found).apply(driver)) {
                    return null;
                }
                return found;
            }

        });

I am working off of version 2.47.2, and the FluentWait appears to retry when null is returned from the delegate Function, so I would expect this to retry if you would otherwise get the StaleElementException.

 public <V> V until(Function<? super T, V> isTrue) {
    long end = clock.laterBy(timeout.in(MILLISECONDS));
    Throwable lastException = null;
    while (true) {
      try {
        V value = isTrue.apply(input);
        if (value != null && Boolean.class.equals(value.getClass())) {
          if (Boolean.TRUE.equals(value)) {
            return value;
          }
        } else if (value != null) {
          return value;
        }
      } catch (Throwable e) {
        lastException = propagateIfNotIngored(e);
      }

      // Check the timeout after evaluating the function to ensure conditions
      // with a zero timeout can succeed.
      if (!clock.isNowBefore(end)) {
        String toAppend = message == null ?
            " waiting for " + isTrue.toString() : ": " + message;

        String timeoutMessage = String.format("Timed out after %d seconds%s",
            timeout.in(SECONDS), toAppend);
        throw timeoutException(timeoutMessage, lastException);
      }

      try {
        sleeper.sleep(interval);
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new WebDriverException(e);
      }
    }

I had seen something similar in one of my tests, and it took me a while but I finally came to the conclusion that in my case it was a race condition between the implicit wait in the WebDriver configuration and the explicit wait time. I've not made a test that proves this case yet, but here's my current theory...

SLEEP

  1. Perform Task to refresh the dom
  2. sleep -- during which dom refreshes
  3. re-request object from refresh dom
  4. Test continues as expected.

Explicit Wait <= Implicit Wait

  1. Perform Task to refresh the dom
  2. Explicit wait triggers (10 seconds), grabs the dom and tries to get the element (Implicit wait of 30 seconds)
  3. Dom Refreshes
  4. Explicit wait ends 10 seconds later. It only tried once to resolve the object against the original DOM
  5. Test fails

Explicit Wait > Implicit Wait

  1. Perform Task to refresh the dom
  2. Explicit wait triggers(30 seconds), grabs the dom and tries to get the element (Implicit wait of 10 seconds)
  3. Dom Refreshes
  4. 10 seconds later the first request fails, the dom refreshes
  5. Explicit wait tries to get the element, and SUCCEEDS
  6. Test Continues.

Working off of that assumption I've not seen this problem recently. I made the Implicit wait value accessible to my test and I now have something calculate the timeframe based on the amount of retries I want to perform.

private final WebElement explicitWait(int retries, Predicate<Boolean> test) {
        WebDriverWait wait = new WebDriverWait(driver, retries * getImplicitWait());
        return wait.until(test);
}

I completely agree with Vinoth about the use and reliability of Thread.sleep, it's unreliable and can be wasteful if it occurs too often in a large suite of tests. It's better to have a mechanism that can respond as quickly as possible and account for well-typed and meaningful exceptions.

Best of Luck.

Upvotes: 1

vins
vins

Reputation: 15370

Thread.sleep is a bad practice because you are using a hardcoded wait. Even if the element appears within 1 second, you are unncessarily wait for 3 more seconds in your case. Or, Lets say the element appears after 10 seconds. you wait for 4 seconds, after that you are going to get an exception that no such element is present. So use expected conditions which is more reliable. Thread.sleep is not reliable. https://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html

WebDriverWait wait = new WebDriverWait(driver, timeout);
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(sXpath)));

WebElement combo=driver.findElement(By.xpath(sXpath));
Select dropdownvalue = new Select(combo);
dropdownvalue.selectByVisibleText(sText);

Upvotes: 0

Related Questions