5

I am writing selenium tests, and following the convention of using Webdriver Waits to have the webdriver poll for an object before it tries to interact with it.

I am frequently finding my tests are failing, and it seems like it is due to a race condition where I am interacting with an element before it has fully loaded.

For example, if I go to my product catalog page, apply some filter, then click on one of the products (loaded by javascript after the filter is applied), sometimes the click will open a pop up, sometimes it won't. My current solution is to throw in Thread.Sleep() to get around the race condition, but I know this is frowned upon.

Has anyone else run into this? What is the best solution? Maybe polling the javascript to verify that the click event has loaded?

3
  • Do you have any throbbers while filters are working and JS is manipulating DOM? Mar 12, 2015 at 20:41
  • Provide the code you have tried with. I am assuming you need explicit wait when finding the element
    – saifur
    Mar 12, 2015 at 20:45
  • @PeterMasiar I have a wait that ensures the filter has been applied before moving on to the next step.
    – GKS1
    Mar 12, 2015 at 21:09

5 Answers 5

2

The thing that helped me sometimes in such cases is to evaluate the state of JQUery: https://stackoverflow.com/questions/20414729/webdriver-wait-for-jquery-to-finish

But of course if you use another library, it becomes tricky.

Additionally you can try to wait for the change you expect at the element, such as the added or removed class or the added content underneath.

The pause/sleep statement is evil but it might be your last resort. Maybe you can give some more insights into what changes.

1
  • 1
    We implemented something like this into our own JavaScript framework in the past. We added a isReady() function we could call from Selenium JavaScript Executor to check if we could continue. Mar 14, 2015 at 12:02
1

Se2 experts suggest NOT USE IMPLICIT WAIT at all. Especially don't mix them with explicit waits (Jim Evans is Se2 core team member). So @kirbycope advice is against opinion of experts (and my own experience). But he correctly advises to search StackOverflow (where I found link above).

What works well for me, is to (explicitly) wait for some other element (by ID), which is available when page is fully loaded. I created my own simple framework, using PageObject design pattern, which uses implicit waits ONLY (wait time is site-wide constant), and my code is more stable.

Implicit ws explicit wait explained

4
  • Hey Peter, thanks for the response. I think the point of my post is being missed though. I am able to correctly wait for the element to exist in the DOM using an explicit wait. However, once the element is there, the wait is over, and my automation tries to click it right away BEFORE the javascript click events are attached to the element. So what is the industry standard for dealing with this race condition? It seems like the standard explicit wait isn't doing the trick for me.
    – GKS1
    Mar 12, 2015 at 20:25
  • Not sure about industry standard, but in such situation waiting additional 1 sec worked for me (after waiting for condition was over). Yes, it is a kludge - but it works and I can move on. Jun 20, 2015 at 0:48
  • Yeah, I just see people writing in blogs "using a sleep is a sign of poorly written code, you should never use a sleep" but then they don't offer what the correct way to do it is. I use the same method. Sleep for 2 seconds, and move on.
    – GKS1
    Jun 22, 2015 at 14:08
  • Also I try to be picky when to sleep, and for how long. Not inside deep loop, for 1 sec only to fire JS events which I cannot capture any other way. I always try to do something smarter than just sleep, but working code beats purity - pick your battles and all that. Jun 22, 2015 at 14:29
0

Explicit Waits are not ideal. Look into using an Implicit Wait. Official documentation, here.

Here is a copy+paste should the URL change or something:

An implicit wait is to tell WebDriver to poll the DOM for a certain amount of time when trying to find an element or elements if they are not immediately available. The default setting is 0. Once set, the implicit wait is set for the life of the WebDriver object instance.

WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement"));

StackOverflow also has a few Q/As for "selenium wait for element".

3
  • Hmm, this doesn't seem to answer my question though. Does the implicit wait know to wait for all the relevant javascript to load? Or will I run into this same race condition that I am seeing with explicit waits?
    – GKS1
    Mar 12, 2015 at 19:43
  • @GKS1, The code/link I provided will help you wait until the element is in the DOM. You could set it up to wait for an element that the JS adds to the DOM.
    – kirbycope
    Mar 12, 2015 at 19:48
  • Maybe my description of the problem isn't clear. The element is added to the DOM, I have no problem waiting for that... But the added element has some click event which is bound after it is already in the DOM. So waiting for it to be in the DOM is not enough, and my selenium test clicks the element too soon (as soon as it is in the DOM, but before the javascript is attached to it). So clicking the element doesn't actually do anything, and the test fails
    – GKS1
    Mar 12, 2015 at 19:51
0

You can try making a custom ExpectedCondition that will repeat whatever process you find unreliable. It works just like any other wait; it just repeats some function until it returns true or times out.

Don't be intimidated by how much code there is. Like most java, it's mostly all just boilerplate you copy/paste :P

In your case you want to click the element and then check if another element changes. If the change occurs, then stop trying and move on.

This is an inelegant example I whipped up to give you the idea.

So in your test you will wait for the click to succeed like so:

By elementToClick = By.cssSelector("foo");
By elementAfterClick = By.cssSelector("bar");
WebDriverWait wait = new WebDriverWait(driver, timeoutInSeconds, attemptIntervalInSeconds);
wait.until(CustomExpectedConditions.ClickSucceeds(elementToClick, elementAfterClick));

Your custom expected conditions class will look like this:

public class CustomExpectedConditions {
 public static ExpectedCondition<Boolean> ClickSucceeds(By preLocator, By postLocator) {
      //use this when you can't trust the the click to always succeed
      return new ClickSucceeds(preLocator, postLocator);
 }

Finally your actual expected condition will look like this: (note: in my example I'm checking if the element exists. You may need to only check isDisplayed())

public class ClickSucceeds implements ExpectedCondition<Boolean> {
    private By locatorToClick;
    private By postClickSuccessLocator;

    public ClickSucceeds(By locatorToClick, By postClickSuccessLocator) {
        this.locatorToClick = locatorToClick;
        this.postClickSuccessLocator = postClickSuccessLocator;
    }

    @Override
    public Boolean apply(WebDriver driver) {
        //todo: make this cleaner
        Boolean success = false;
        if (!success) {
            try {
                WebElement targetElement = driver.findElement(this.locatorToClick);
                targetElement.click();
                success = !(driver.findElements(this.postClickSuccessLocator).isEmpty());
                //or success = driver.findElement(postClickSuccessLocator).isDisplayed();
            } catch (NoSuchElementException e) {
                return null;
            }
        }
        else {
            return success;
        }
        return null;
    }
}

Cheers, hope that helps!

0

Here is the ultimate solution specifically when you are dealing with Angular 7 or 8.

Instead of waiting for a longer duration using sleep or implicit wait methods, you can divide your wait time into the partition and use it recursively.

Below logic will wait for the page to render for a minimum of 300 seconds and a maximum of 900 seconds.

/**
 * This method will check page loading
 *
 */
public void waitForLoadingToComplete() {
    waitLoadingTime(3); // Enter the number of attempts you want to try
}

 private void waitLoadingTime(int i) {
        try {
  // wait for the loader to appear after particular action/click/navigation
            this.staticWait(300); 
  // check for the loader and wait till loader gets disappear
            waitForElementToBeNotPresent(By.cssSelector("Loader Element CSS"));                                                             
        } catch (org.openqa.selenium.TimeoutException e) {
            if (i != 0)
                waitLoadingTime(i - 1);
        }
    }

/**
 * This method is for the static wait
 *
 * @param millis
 */
public void staticWait(final long millis) {
    try {
        TimeUnit.MILLISECONDS.sleep(millis);
    } catch (final InterruptedException e) {
        System.err.println("Error in staticWait." + e);
    }
}

public void waitForElementToBeNotPresent(final By element) {
    long s = System.currentTimeMillis();
    new WebDriverWait(this.getDriver(), 30)
            .until(ExpectedConditions.not(ExpectedConditions.presenceOfAllElementsLocatedBy(element)));
    System.err.println("Waiting for Element to be not present completed. " + (System.currentTimeMillis() - s));
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.