Waiting for things to happen

Synchronising your code with the page using explicit waits and expected conditions, and why a fixed sleep is the wrong tool.

Your script clicks a button, and the next line fails because the element it needs has not appeared yet. Run it again and it passes. Modern applications update the page asynchronously, so the browser and your code are in a race, and the loser is a flaky test.

Use an explicit wait: a loop that polls the application for a specific condition and continues as soon as that condition is true, or raises a timeout error if it never is. You decide what to wait for at each point where synchronisation matters, so the wait is exactly as long as it needs to be and no longer.

Most bindings ship a library of ready-made expected conditions (element visible, title contains, alert present, and so on) that you can pass to the wait instead of writing your own lambda. Each of them is a function that is called repeatedly until it returns a truthy value.

A fixed sleep is the first thing many people reach for, and it is nearly always wrong. It is either too short, and the test fails on a slow machine, or too long, and every test in the suite pays the full price every time. Likewise, do not combine an implicit wait with explicit waits: the two timeouts add up in ways that are hard to predict.

This example waits for an element that is revealed a couple of seconds after a click:

    Wait<WebDriver> wait = new WebDriverWait(driver, Duration.ofSeconds(2));
    wait.until(d -> revealed.isDisplayed());
    wait = WebDriverWait(driver, timeout=2)
    wait.until(lambda _ : revealed.is_displayed())
            WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(2));
            wait.Until(d => revealed.Displayed);
    wait = Selenium::WebDriver::Wait.new
    wait.until { revealed.displayed? }
        await driver.wait(until.elementIsVisible(revealed), 2000);
        val wait =  WebDriverWait(driver, Duration.ofSeconds(2))
        wait.until { revealed.isDisplayed }

The same wait expressed with a built-in expected condition:

    wait = WebDriverWait(driver, timeout=2)
    wait.until(EC.visibility_of_element_located((By.ID, "revealed")))

Common errors

Reference