Reputation: 156
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
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
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")));
driver.findElement()
will wait for 10 seconds untill element is located before throwing NoSuchElementException
. This is a very important point to note.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
.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;
}
}
};
}
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
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