armani
armani

Reputation: 156

Selenium - Is it okay to mix implicit wait and explicit wait like this?

Here is a udemy course (from "Lets Kode It") to develop a web automation framework with selenium and Java. But, this is not a java question. You only need to know selenium in any of these languages - javascript, python, ruby, c# & java.

The instructor has developed a CustomDriver class which has the method/function given below. The method waits for an element to be clickable, without having to write WebDriverWait statements everywhere in our code. It first sets the implicit wait to zero, does an explicit wait and then sets the implicit wait to the original value which was being used in the framework.

This approach seems okay to me, but I am not sure about it. Could mixing implicit and explicit waits like this cause any problems?

UPDATE (March 24, 2020) - I already know that mixing implicit and explicit waits is considered a bad practice because it can lead to unpredictable wait times. I am not asking about the unpredictable wait times because there are plenty of questions and articles on that already.

Instead, I am asking that if the implicit wait is set to zero every time before doing an explicit wait, then is that okay? Will that still cause the problems of unpredictable waits? Will it cause other problems ?

/*
 Click element when element is clickable
 @param locator - locator strategy, id=>example, name=>example, css=>#example,
                       tag=>example, xpath=>//example, link=>example
 @param timeout - Duration to try before timeout
 */
public void clickWhenReady(By locator, int timeout) {
    try {
        driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
        WebElement element = null;
        System.out.println("Waiting for max:: " + timeout + " seconds for element to be clickable");

        WebDriverWait wait = new WebDriverWait(driver, 15);
        element = wait.until(
                ExpectedConditions.elementToBeClickable(locator));
        element.click();
        System.out.println("Element clicked on the web page");
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
    } catch (Exception e) {
        System.out.println("Element not appeared on the web page");
        driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
    }
}

Upvotes: 4

Views: 4905

Answers (3)

Ardesco
Ardesco

Reputation: 7441

When we say mixing implicit and explicit waits is bad, what we actually mean is that you should not have a non-zero implicit wait value set whilst you are using an explicit wait.

If you reset the implicit wait value to 0 for the driver object used by the explicit wait before you invoke the explicit wait (i.e. you actually start waiting), technically it should work fine, because you aren't really mixing the implicit and explicit waits. This is a viable strategy if you can pull it off.

Now that being said, it can be a lot of work to keep tweaking the implicit wait value, and checking that you are accurately keep track of it. It's even worse in a shared codebase where another person coming along doesn't know that this is your strategy (or doesn't understand it). This is why the general advice is just don't use implicit waits, use explicit ones instead.

Secondly, have a look at what you are actually doing; if you are explicitly changing the implicit wait value before every interaction where you want to wait for a specific element to appear, you really aren't that far off using an explicit wait. I would suggest that by using an explicit wait, it will make the intent of your code clearer to those reading it in the future and as a result easier to maintain.

Upvotes: 2

StrikerVillain
StrikerVillain

Reputation: 3776

The problem of using implicit wait and explicit wait boils down to the flaws in how ExpectedConditions are implemented in Selenium source code.

Let me explain the problem by analyzing the below code:

WebDriver driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
WebElement element =  new WebDriverWait(driver, 5, 1000).until(ExpectedConditions.elementToBeClickable(By.id("some_id")));
  1. We initialize the driver and set implicit wait to 10 seconds. This means that the driver.findElement() will wait for 10 seconds untill element is located before throwing NoSuchElementException. This is a very important point to note.
  2. Then we set up an explicit wait with 5 seconds duration and sleep time between polling of 1000 milliseconds (1 second). This means that the WebDriverWait will poll the ExpectedConditions to be true every 1 second till 5 second. If ExpectedConditions return true, the polling will stop and the Object specified in ExpectedConditions is returned. In the example code above returned object is a WebElement. In case ExpectedConditions is false after 5 seconds, a TimeoutException is thrown (OR SO WE EXPECT). Now time to see what happens in ExpectedConditions.
  3. The ExpectedConditions.elementToBeClickable() code has the below syntax.

    public static ExpectedCondition<WebElement> elementToBeClickable(final By locator) {
        return new ExpectedCondition<WebElement>() {
            @Override
            public WebElement apply(WebDriver driver) {
                WebElement element = visibilityOfElementLocated(locator).apply(driver);
                try {
                    if (element != null && element.isEnabled()) {
                        return element;
                    }
                    return null;
                } catch (StaleElementReferenceException e) {
                    return null;
                }
            }
        };
    }
    
  4. The elementToBeClickable above in turn calls visibilityOfElementLocated() method to confirm if element is visible.

    public static ExpectedCondition<WebElement> visibilityOfElementLocated(final By locator) {
        return new ExpectedCondition<WebElement>() {
            @Override
            public WebElement apply(WebDriver driver) {
                try {
                    return elementIfVisible(driver.findElement(locator));
                } catch (StaleElementReferenceException e) {
                    return null;
                }
            }
        };
    }
    

    5.Notice how driver.findElement(locator) is invoked above in the visibilityOfElementLocated() method. In case the element is not found the implicit wait of 10 seconds will apply. So driver will wait for 10 seconds untill it throws NoSuchElementException.

But wait (pun not intentional)! isn't our explicit wait set to timeout at 5 seconds on elementToBeClickable() condition? Yes it is, but the implicit wait will be applied first. The WebDriverWait will catch the NoSuchElementException and throw a TimeoutException after 10 seconds instead of the set explicit wait of 5 seconds. Therein lies the problem which the solution in the question is trying to address. The solution attempts to set the implicit wait to 0 seconds so the explicit wait condition is executed correctly and then resets the implicit wait.

The implementation provided in the Question does get the job done sans one detail. The implicit wait is hard coded to 3 seconds which is not ideal. How you provide the implicit wait as a global universal constant is very case specific. We are setting implicit wait on driver and we can expect the implicit wait like 'driver' driver.manage().timeouts().getImplicitWait(). Though ideal, unfortunately this is NOT possible directly. There are workarounds, @forresthopkinsa has a pretty interesting solution on creating extended drivers for getting implicit waits

UPDATE (March 24, 2020)

Q: This approach seems okay to me, but I am not sure about it. Could mixing implicit and explicit waits like this cause any problems?

I am asking that if the implicit wait is set to zero every time before doing an explicit wait, then is that okay? Will that still cause the problems of unpredictable waits? Will it cause other problems ?

As far as I understand, there should not be any problems with setting implicit wait to 0 and then performing explicit wait and then switching back as there is no restriction on setting implicit wait at any point in time during test execution.

Also, if you think about it you are really not mixing implicit wait and explicit wait in the solution from code execution point of view. In fact you are doing the opposite! If you have set implicit wait to a certain non-zero value and then performed explicit wait, that is where the real mixing happens. First implicit is executed if applicable and then explicit wait executes leading to inconsistencies. If you set implicit wait to 0, you take out the implicit wait from the timeout equation!

The problem of unpredictable waits would not occur with the solution in OP.

Upvotes: 2

supputuri
supputuri

Reputation: 14135

I would not suggest mix them. As Implicit wait is most of the times implemented at the remote side of the WebDriver system meaning they are handled in the browser based driver (eg: chromedriver.exe, IEDriverServer.exe), where as Explicit Wait is implement on the local language bindings like Java, ruby, python etc.

Below is the typical example what happens when your run the script with remote server.

Local Code -> Remote server -> Local language bindings on the remote server -> remote component like chromedriver.exe or IEDriverServer.exe. Things get more complex when if you have grid involved as it could be another layer in between the chain.

So when you mix both the Implicit and Explicit waits, you might end-up with undefined behavior. And moreover, as Implicit waits are implemented at the driver level they might change anytime and have impact on your scripts. So, it's always better to stick to Explicit wait and have total control.

With the current technologies, the elements might render lately after the element is present. So, going with implicit wait is not sufficient, hence strongly prefer using explicit wait. There may be some edge cases we might have to use implicit wait, but never mix both of them if you are planning to extend your script in future to run on grid/using remote server.

Upvotes: 3

Related Questions