How to synchronise on application state? How to Wait with WebDriver? Thread.sleep is not the answer, so what is?
Avoid Thread.sleep
The main thing we want to avoid is a Thread.sleep
If you do use Thread.sleep
make it obvious what you are doing.
But do not rely on this for strategic automating.
private void pauseToAllowVisibility() {
try{
Thread.sleep(1000);
}catch(Exception e){
// ignore
}
}
For more information about Thread.sleep, check this blog post.
Waiting
Waiting in WebDriver uses a WebDriverWait
driver.get("https://testpages.herokuapp.com/styled/calculator");
new WebDriverWait(driver,10).
until(
// some ExpectedCondition to wait for
);
And the WebDriverWait
can be assigned to a variable for re-use.
e.g.
WebDriverWait wait = new WebDriverWait(driver,10);
ExpectedConditions
I remember back in 2012 reading through the WebDriver code and finding a support class for ExpectedConditions
class which exposes a bunch of static methods to save me having to write code.
ExpectedConditions
contains a lot of common waiting conditions.
The ExpectedConditions
class has a method waiting for a page title to contain some text.
driver.get("https://testpages.herokuapp.com/styled/calculator");
new WebDriverWait(driver,10).
until(ExpectedConditions.titleContains("Selenium"));
Creating Your Own ExpectedCondition
I can create and use my own ExpectedCondition
classes:
driver.get("https://testpages.herokuapp.com/styled/calculator");
new WebDriverWait(driver,10).
until(new TitleContainsCondition("Selenium"));
This opens the driver to the page and waits until the Title contains the text “Selenium”.
And I would write code to support this i.e. my TitleContainsCondition
class:
public class TitleContainsCondition implements ExpectedCondition {
private String subMenuText;
public TitleContainsCondition(final String subMenuText) {
this.subMenuText=subMenuText;
}
@Override
public Boolean apply(@Nullable WebDriver driver) {
return driver.getTitle().contains(this.subMenuText);
}
}
Create Your Own ExpectedConditions
If I want a custom ExpectedConditions
then I can create my own ‘factory’ class to return ExpectedCondition
driver.get("https://testpages.herokuapp.com/styled/calculator");
new WebDriverWait(driver,10).
until(WaitFor.titleContainsCondition("Selenium"));
I do occasionally write a WaitFor
Class to add application or non-standard conditions, since we test applications which are domain specific.
And the WaitFor
would look like:
public class WaitFor {
public static ExpectedCondition<Boolean> titleContainsCondition(String titleMustContain) {
return new TitleContainsCondition(titleMustContain);
}
}
Inline
If I really wanted to I could use a WebDriverWait
without a separate class.
@Test
public void useInLineExpectedCondition(){
driver.get("https://testpages.herokuapp.com/styled/calculator");
new WebDriverWait(driver,10)
.until(
new ExpectedCondition<Boolean>(){
@Override
public Boolean apply(WebDriver driver) {
return driver.getTitle().startsWith("Selenium");
}
}
);
}
But I haven’t written any code like this for some time. If I use a wait then I would refactor it into a class of its own, or wrap it as a factory method e.g. on WaitFor
Lambda
With Java 8 it is now possible to use waits in closure/lambda expressions.
@Test
public void useViaLambdaExpressions(){
driver.get("https://testpages.herokuapp.com/styled/calculator");
WebDriverWait wait = new WebDriverWait(driver,10);
ExpectedCondition<Boolean> titleStartsWithSelenium = mydriver ->
{ return mydriver.getTitle().startsWith("Selenium");};
wait.until(titleStartsWithSelenium);
}
I rarely use this, but sometimes for adhoc experimentation or workarounds I do.
Historical Note
WebDriver
initial implementation of waiting was much more basic.
e.g. here is some code from 2011(ish)
new Wait("JS Page title did not change"){
@Override
public boolean until() {
try{
return driver.getTitle().startsWith("Selenium"));
}catch(Exception e){
// ignore not found exception
}
return false;
}
};
Note: this post was originally written in 2011, hence the ‘historical note’ example, and updated on 20200603.
Full Source
The full source for this is in my Webdriver Java FAQs project:
Specifically:
If you want to learn how to use Selenium WebDriver with Java then check out our online courses. We have course dedicated to Advanced Synchronisation, and one dedicated to the Support Classes.
You will need a Github account to comment. Or you can contact me with your comment.
I reserve the right to delete spam comments e.g. if your comment adds no value and its purpose is simply to create a backlink to another site offering training, or courses, etc.