Add some ‘Weight’ to your Selenium Tests with ‘Waits’! – Part 2: Explicit Waits

In Part 1 of this series on Waits in Selenium (here) we had a look at Implicit Waits. We observed that whilst Implicit Waits are a good way to quickly and easily add some waits to your Selenium tests, they are far from perfect and they do raise some problems for us. Today we are going to learn about another type of waits – Explicit Waits. These types of waits are recommended to be used by the official Selenium documentation, so it seems that they are well worth checking out!

Explicit waits allow us a lot more flexibility than Implicit Waits, but they do have a steeper learning curve to fully understand. In a nutshell, explicit waits are used to halt the execution of the script until a particular condition is met or the maximum time has elapsed. Unlike Implicit waits, Explicit waits are applied for a particular instance only.

When we use Explicit waits, we are going to use them in conjunction with the ExpectedConditions class. This class has several methods that can be used to wait for a certain condition to be met. I don’t want to overload you with information on the ExpectedConditions class at this stage, especially if it is new to you. We are going to look at a couple of examples for ExpectedConditions in a moment, but if you want to know all the methods that the class has, you can read up on them here .

As always, the best way to explain a concept is with an example, so let’s work through one now. We are again going to use the Dynamic Loading page on ‘The Internet’ (here) . Create a new test class in your ‘test > java’ folder as normal and call it ‘MyFirstExplicitWait‘.

Go ahead and add the following setup code to the class:

public class MyFirstExplictWait {

    public static FirefoxDriver driver;

    @BeforeClass
    public static void createDriver() {
        driver = new FirefoxDriver();
    }

    @AfterClass
    public static void quitDriver() {
        driver.quit();
    }

}

There is nothing new in the code above and it doesn’t do anything yet, but notice that we have not setup any waits yet. Add the following @Test in between the @BeforeClass and @AfterClass, and then we will talk through it line by line to work out what is going on:

@Test
public void explicitWaitTest()
{
    driver.get("http://the-internet.herokuapp.com/dynamic_loading/2");

    WebDriverWait waitForStartButton = new WebDriverWait(driver, 2);

    waitForStartButton.until(ExpectedConditions.elementToBeClickable(By.cssSelector("#start>button")));

    WebElement startButton = driver.findElement(By.cssSelector("#start>button"));

    startButton.click();

    WebDriverWait waitForHelloText = new WebDriverWait(driver, 10);

    waitForHelloText.until(ExpectedConditions.visibilityOfElementLocated(By.id("finish")));

    WebElement helloWorldText = driver.findElement(By.id("finish"));

    String textFromElement = helloWorldText.getText();

    assertThat(textFromElement, equalTo("Hello World!"));
}

The test starts off by getting the page at http://the-internet.herokuapp.com/dynamic_loading/2 , nothing exciting there. On the next two lines, we have some new code:

WebDriverWait waitForStartButton = new WebDriverWait(driver, 2);

waitForStartButton.until(ExpectedConditions.elementToBeClickable(By.cssSelector("#start>button")));

Here we are setting up a WebDriverWait just for the Start button on the page. We have not actually tied it to the start button, but we have linked it to our ‘driver’ and given it a timeout of 2 seconds.

On the 2nd line, we call the ‘waitForStartButton‘ wait and then we call the ‘.until()‘ method. We then pass in the ExpectedConditions class. From that class we call the ‘.elementToBeClickable‘ method, which as the name suggests waits until an element on the page is clickable. Finally we pass in the locator that looks for element on the page, in this case ‘By.cssSelector(“#start>button”)‘.

So to recap, the above two lines of code will wait for a maximum of 2 seconds until the Start Button on the page is clickable. If the wait passes then the test will continue, else it will fail at this point (so it will fail if the start button does not become clickable within 2 seconds).

The next two lines of code will click the start button for us:

WebElement startButton = driver.findElement(By.cssSelector("#start>button"));

startButton.click();

There is nothing new here (we looked at this code in the previous post – here), but we can feel confident that if this code is being executed then the startButton is indeed on the page and it is clickable, because the previous wait code that we wrote has passed.

On the next two lines we are setting up a wait for the ‘Hello World!‘ text to appear:

WebDriverWait waitForHelloText = new WebDriverWait(driver, 10);

waitForHelloText.until(ExpectedConditions.visibilityOfElementLocated(By.id("finish")));

This time, we setup a WebDriverWait called ‘waitForHelloText‘. We pass in the driver again, but this time we give the wait a timeout of 10 seconds.

We now call the ‘waitForHelloText’ wait and again call the ‘.until()‘ method. We again pass in the ‘ExpectedConditions‘ class and this time we call the method ‘visibilityOfElementLocated‘. As the name suggests, this will simply wait until an element is visible by looking for the locator we provide. We provide a locator of ‘By.id(“finish”)‘.

To recap the above two lines again, we will wait for a maximum of 10 seconds until the ‘Hello World!‘ text is visible.

The final three lines of text simply grab the text from the ‘Hello World!’ element, and check that it is correct:

WebElement helloWorldText = driver.findElement(By.id("finish"));

String textFromElement = helloWorldText.getText();

assertThat(textFromElement, equalTo("Hello World!"));

There is nothing here that is new to us, but again we can be confident that the ‘Hello World’ element will be visible on the screen, because the previous wait code was executed successfully (remember, if there was a problem with the code before, then the test will have failed and the execution would not have got to this point).

Go ahead and run the test, it should pass in an efficient time.

Now at this stage you might be a bit confused by the above, especially if you are relatively new to programming. It took me longer than I care to admit to get my head around it! All I can tell you is keep practicing and keep trying. The good above is a solid example that you can use for a base when writing Explicit waits. Feel free to read it again (and make sure you actually type it out, don’t just copy and paste the code!) until you have a better understanding.

Another thing you might be thinking at this stage is that Explicit waits are a lot of work, compared to an Implicit wait that we just set once at the top of the class. Whilst that it is partially true, the extra flexibility and reliability that they afford us makes them well worth it.

In an upcoming post, we will look at a slightly more advanced way of using Explicit waits by writing our own wait methods! Going forward this will makes our tests both more readable and easier to write, since we won’t need to declare a new WebDriverWait every single time. It will be more advanced though, since we will be writing our own methods for the first time, which is code that is executed outside of the normal @Test annotations. Before we do that, we will first look at another type of waits called Fluent Waits.

Click here to go to Part 3: Fluent Waits