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.
Recommended approach 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:
Java
Python
CSharp
Ruby
JavaScript
Kotlin Wait < WebDriver > wait = new WebDriverWait ( driver , Duration . ofSeconds ( 2 ));
wait . until ( d -> revealed . isDisplayed ()); /examples/java/src/test/java/dev/selenium/waits/WaitsTest.java
Copy
Close
package dev.selenium.waits ;
import dev.selenium.BaseTest ;
import java.time.Duration ;
import org.junit.jupiter.api.Assertions ;
import org.junit.jupiter.api.Test ;
import org.openqa.selenium.By ;
import org.openqa.selenium.ElementNotInteractableException ;
import org.openqa.selenium.NoSuchElementException ;
import org.openqa.selenium.WebDriver ;
import org.openqa.selenium.WebElement ;
import org.openqa.selenium.chrome.ChromeOptions ;
import org.openqa.selenium.support.ui.FluentWait ;
import org.openqa.selenium.support.ui.Wait ;
import org.openqa.selenium.support.ui.WebDriverWait ;
public class WaitsTest extends BaseTest {
@Test
public void fails () {
startChromeDriver ( new ChromeOptions ());
driver . get ( "https://www.selenium.dev/selenium/web/dynamic.html" );
driver . findElement ( By . id ( "adder" )). click ();
Assertions . assertThrows (
NoSuchElementException . class ,
() -> {
driver . findElement ( By . id ( "box0" ));
});
}
@Test
public void sleep () throws InterruptedException {
startChromeDriver ( new ChromeOptions ());
driver . get ( "https://www.selenium.dev/selenium/web/dynamic.html" );
driver . findElement ( By . id ( "adder" )). click ();
Thread . sleep ( 1000 );
WebElement added = driver . findElement ( By . id ( "box0" ));
Assertions . assertEquals ( "redbox" , added . getDomAttribute ( "class" ));
}
@Test
public void implicit () {
startChromeDriver ( new ChromeOptions ());
driver . manage (). timeouts (). implicitlyWait ( Duration . ofSeconds ( 2 ));
driver . get ( "https://www.selenium.dev/selenium/web/dynamic.html" );
driver . findElement ( By . id ( "adder" )). click ();
WebElement added = driver . findElement ( By . id ( "box0" ));
Assertions . assertEquals ( "redbox" , added . getDomAttribute ( "class" ));
}
@Test
public void explicit () {
startChromeDriver ( new ChromeOptions ());
driver . get ( "https://www.selenium.dev/selenium/web/dynamic.html" );
WebElement revealed = driver . findElement ( By . id ( "revealed" ));
driver . findElement ( By . id ( "reveal" )). click ();
Wait < WebDriver > wait = new WebDriverWait ( driver , Duration . ofSeconds ( 2 ));
wait . until ( d -> revealed . isDisplayed ());
revealed . sendKeys ( "Displayed" );
Assertions . assertEquals ( "Displayed" , revealed . getDomProperty ( "value" ));
}
@Test
public void explicitWithOptions () {
startChromeDriver ( new ChromeOptions ());
driver . get ( "https://www.selenium.dev/selenium/web/dynamic.html" );
WebElement revealed = driver . findElement ( By . id ( "revealed" ));
driver . findElement ( By . id ( "reveal" )). click ();
Wait < WebDriver > wait =
new FluentWait <> ( driver )
. withTimeout ( Duration . ofSeconds ( 2 ))
. pollingEvery ( Duration . ofMillis ( 300 ))
. ignoring ( ElementNotInteractableException . class );
wait . until (
d -> {
revealed . sendKeys ( "Displayed" );
return true ;
});
Assertions . assertEquals ( "Displayed" , revealed . getDomProperty ( "value" ));
}
}
wait = WebDriverWait ( driver , timeout = 2 )
wait . until ( lambda _ : revealed . is_displayed ()) /examples/python/tests/waits/test_waits.py
Copy
Close
import pytest
import time
from selenium.common import NoSuchElementException , ElementNotInteractableException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
def test_fails ( driver ):
driver . get ( 'https://www.selenium.dev/selenium/web/dynamic.html' )
driver . find_element ( By . ID , "adder" ) . click ()
with pytest . raises ( NoSuchElementException ):
driver . find_element ( By . ID , 'box0' )
def test_sleep ( driver ):
driver . get ( 'https://www.selenium.dev/selenium/web/dynamic.html' )
driver . find_element ( By . ID , "adder" ) . click ()
time . sleep ( 2 )
added = driver . find_element ( By . ID , "box0" )
assert added . get_dom_attribute ( 'class' ) == "redbox"
def test_implicit ( driver ):
driver . implicitly_wait ( 2 )
driver . get ( 'https://www.selenium.dev/selenium/web/dynamic.html' )
driver . find_element ( By . ID , "adder" ) . click ()
added = driver . find_element ( By . ID , "box0" )
assert added . get_dom_attribute ( 'class' ) == "redbox"
def test_explicit ( driver ):
driver . get ( 'https://www.selenium.dev/selenium/web/dynamic.html' )
revealed = driver . find_element ( By . ID , "revealed" )
driver . find_element ( By . ID , "reveal" ) . click ()
wait = WebDriverWait ( driver , timeout = 2 )
wait . until ( lambda _ : revealed . is_displayed ())
revealed . send_keys ( "Displayed" )
assert revealed . get_property ( "value" ) == "Displayed"
def test_explicit_options ( driver ):
driver . get ( 'https://www.selenium.dev/selenium/web/dynamic.html' )
revealed = driver . find_element ( By . ID , "revealed" )
driver . find_element ( By . ID , "reveal" ) . click ()
errors = [ NoSuchElementException , ElementNotInteractableException ]
wait = WebDriverWait ( driver , timeout = 2 , poll_frequency = .2 , ignored_exceptions = errors )
wait . until ( lambda _ : revealed . send_keys ( "Displayed" ) or True )
assert revealed . get_property ( "value" ) == "Displayed"
WebDriverWait wait = new WebDriverWait ( driver , TimeSpan . FromSeconds ( 2 ));
wait . Until ( d => revealed . Displayed ); /examples/dotnet/SeleniumDocs/Waits/WaitsTest.cs
Copy
Close
using System ;
using System.Threading ;
using Microsoft.VisualStudio.TestTools.UnitTesting ;
using OpenQA.Selenium ;
using OpenQA.Selenium.Support.UI ;
namespace SeleniumDocs.Waits
{
[TestClass]
public class WaitsTest : BaseChromeTest
{
[TestMethod]
public void Fails ()
{
driver . Url = "https://www.selenium.dev/selenium/web/dynamic.html" ;
driver . FindElement ( By . Id ( "adder" )). Click ();
Assert . ThrowsException < NoSuchElementException >(
() => driver . FindElement ( By . Id ( "box0" ))
);
}
[TestMethod]
public void Sleep ()
{
driver . Url = "https://www.selenium.dev/selenium/web/dynamic.html" ;
driver . FindElement ( By . Id ( "adder" )). Click ();
Thread . Sleep ( 1000 );
IWebElement added = driver . FindElement ( By . Id ( "box0" ));
Assert . AreEqual ( "redbox" , added . GetDomAttribute ( "class" ));
}
[TestMethod]
public void Implicit ()
{
driver . Manage (). Timeouts (). ImplicitWait = TimeSpan . FromSeconds ( 2 );
driver . Url = "https://www.selenium.dev/selenium/web/dynamic.html" ;
driver . FindElement ( By . Id ( "adder" )). Click ();
IWebElement added = driver . FindElement ( By . Id ( "box0" ));
Assert . AreEqual ( "redbox" , added . GetDomAttribute ( "class" ));
}
[TestMethod]
public void Explicit ()
{
driver . Url = "https://www.selenium.dev/selenium/web/dynamic.html" ;
IWebElement revealed = driver . FindElement ( By . Id ( "revealed" ));
driver . FindElement ( By . Id ( "reveal" )). Click ();
WebDriverWait wait = new WebDriverWait ( driver , TimeSpan . FromSeconds ( 2 ));
wait . Until ( d => revealed . Displayed );
revealed . SendKeys ( "Displayed" );
Assert . AreEqual ( "Displayed" , revealed . GetDomProperty ( "value" ));
}
[TestMethod]
public void ExplicitOptions ()
{
driver . Url = "https://www.selenium.dev/selenium/web/dynamic.html" ;
IWebElement revealed = driver . FindElement ( By . Id ( "revealed" ));
driver . FindElement ( By . Id ( "reveal" )). Click ();
WebDriverWait wait = new WebDriverWait ( driver , TimeSpan . FromSeconds ( 2 ))
{
PollingInterval = TimeSpan . FromMilliseconds ( 300 ),
};
wait . IgnoreExceptionTypes ( typeof ( ElementNotInteractableException ));
wait . Until ( d => {
revealed . SendKeys ( "Displayed" );
return true ;
});
Assert . AreEqual ( "input" , revealed . TagName );
}
}
} wait = Selenium :: WebDriver :: Wait . new
wait . until { revealed . displayed? } /examples/ruby/spec/waits/waits_spec.rb
Copy
Close
# frozen_string_literal: true
require 'spec_helper'
RSpec . describe 'Waits' do
let ( :driver ) { start_session }
it 'fails' do
driver . get 'https://www.selenium.dev/selenium/web/dynamic.html'
driver . find_element ( id : 'adder' ) . click
expect {
driver . find_element ( id : 'box0' )
} . to raise_error ( Selenium :: WebDriver :: Error :: NoSuchElementError )
end
it 'sleeps' do
driver . get 'https://www.selenium.dev/selenium/web/dynamic.html'
driver . find_element ( id : 'adder' ) . click
sleep 1
added = driver . find_element ( id : 'box0' )
expect ( added . dom_attribute ( :class )) . to eq ( 'redbox' )
end
it 'implicit' do
driver . manage . timeouts . implicit_wait = 2
driver . get 'https://www.selenium.dev/selenium/web/dynamic.html'
driver . find_element ( id : 'adder' ) . click
added = driver . find_element ( id : 'box0' )
expect ( added . dom_attribute ( :class )) . to eq ( 'redbox' )
end
it 'explicit' do
driver . get 'https://www.selenium.dev/selenium/web/dynamic.html'
revealed = driver . find_element ( id : 'revealed' )
driver . find_element ( id : 'reveal' ) . click
wait = Selenium :: WebDriver :: Wait . new
wait . until { revealed . displayed? }
revealed . send_keys ( 'Displayed' )
expect ( revealed . property ( :value )) . to eq ( 'Displayed' )
end
it 'options with explicit' do
driver . get 'https://www.selenium.dev/selenium/web/dynamic.html'
revealed = driver . find_element ( id : 'revealed' )
driver . find_element ( id : 'reveal' ) . click
errors = [ Selenium :: WebDriver :: Error :: NoSuchElementError ,
Selenium :: WebDriver :: Error :: ElementNotInteractableError ]
wait = Selenium :: WebDriver :: Wait . new ( timeout : 2 ,
interval : 0 . 3 ,
ignore : errors )
wait . until { revealed . send_keys ( 'Displayed' ) || true }
expect ( revealed . property ( :value )) . to eq ( 'Displayed' )
end
end
await driver . wait ( until . elementIsVisible ( revealed ), 2000 ); /examples/javascript/test/waits/waits.spec.js
Copy
Close
const { By , Browser , until , Builder } = require ( 'selenium-webdriver' );
const assert = require ( "node:assert" );
describe ( 'Waits' , function () {
let driver ;
before ( async function () {
driver = new Builder ()
. forBrowser ( Browser . CHROME )
. build ();
});
after ( async () => await driver . quit ());
it ( 'fail' , async function () {
await driver . get ( 'https://www.selenium.dev/selenium/web/dynamic.html' );
await driver . findElement ( By . id ( "adder" )). click ();
await assert . rejects ( async () => {
await driver . findElement ( By . id ( "box0" ))
},
Error
)
});
it ( 'sleep' , async function () {
await driver . get ( 'https://www.selenium.dev/selenium/web/dynamic.html' );
await driver . findElement ( By . id ( "adder" )). click ();
await driver . sleep ( 2000 );
let added = await driver . findElement ( By . id ( "box0" ));
assert . equal ( await added . getAttribute ( 'class' ), "redbox" )
});
it ( 'implicit' , async function () {
await driver . manage (). setTimeouts ({ implicit : 2000 });
await driver . get ( 'https://www.selenium.dev/selenium/web/dynamic.html' );
await driver . findElement ( By . id ( "adder" )). click ();
let added = await driver . findElement ( By . id ( "box0" ));
assert . equal ( await added . getAttribute ( 'class' ), "redbox" )
});
it ( 'explicit' , async function () {
await driver . get ( 'https://www.selenium.dev/selenium/web/dynamic.html' );
let revealed = await driver . findElement ( By . id ( "revealed" ));
await driver . findElement ( By . id ( "reveal" )). click ();
await driver . wait ( until . elementIsVisible ( revealed ), 2000 );
await revealed . sendKeys ( "Displayed" );
assert . equal ( await revealed . getAttribute ( "value" ), "Displayed" )
})
}); val wait = WebDriverWait ( driver , Duration . ofSeconds ( 2 ))
wait . until { revealed . isDisplayed } /examples/kotlin/src/test/kotlin/dev/selenium/waits/WaitsTest.kt
Copy
Close
package dev.selenium.waits
import dev.selenium.BaseTest
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.openqa.selenium.By
import org.openqa.selenium.ElementNotInteractableException
import org.openqa.selenium.WebDriver
import org.openqa.selenium.support.ui.FluentWait
import org.openqa.selenium.support.ui.Wait
import org.openqa.selenium.support.ui.WebDriverWait
import java.time.Duration
class WaitsTest : BaseTest () {
@Test
fun implicit () {
driver . manage (). timeouts (). implicitlyWait ( Duration . ofSeconds ( 2 ))
driver . get ( "https://www.selenium.dev/selenium/web/dynamic.html" )
driver . findElement ( By . id ( "adder" )). click ()
val added = driver . findElement ( By . id ( "box0" ))
Assertions . assertEquals ( "redbox" , added . getDomAttribute ( "class" ))
}
@Test
fun explicit () {
driver . get ( "https://www.selenium.dev/selenium/web/dynamic.html" )
val revealed = driver . findElement ( By . id ( "revealed" ))
driver . findElement ( By . id ( "reveal" )). click ()
val wait = WebDriverWait ( driver , Duration . ofSeconds ( 2 ))
wait . until { revealed . isDisplayed }
revealed . sendKeys ( "Displayed" )
Assertions . assertEquals ( "Displayed" , revealed . getDomProperty ( "value" ))
}
@Test
fun explicitWithOptions () {
driver . get ( "https://www.selenium.dev/selenium/web/dynamic.html" )
val revealed = driver . findElement ( By . id ( "revealed" ))
driver . findElement ( By . id ( "reveal" )). click ()
val wait : Wait < WebDriver > =
FluentWait ( driver )
. withTimeout ( Duration . ofSeconds ( 2 ))
. pollingEvery ( Duration . ofMillis ( 300 ))
. ignoring ( ElementNotInteractableException :: class . java )
wait . until {
revealed . sendKeys ( "Displayed" )
true
}
Assertions . assertEquals ( "Displayed" , revealed . getDomProperty ( "value" ))
}
} 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" ))) /examples/python/tests/support/test_expected_conditions.py
Copy
Close
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
# Expected Conditions API Documentation:
# https://www.selenium.dev/selenium/docs/api/py/webdriver_support/selenium.webdriver.support.expected_conditions.html
def test_expected_condition ( driver ):
driver . get ( "https://www.selenium.dev/selenium/web/dynamic.html" )
revealed = driver . find_element ( By . ID , "revealed" )
driver . find_element ( By . ID , "reveal" ) . click ()
wait = WebDriverWait ( driver , timeout = 2 )
wait . until ( EC . visibility_of_element_located (( By . ID , "revealed" )))
revealed . send_keys ( "Displayed" )
assert revealed . get_property ( "value" ) == "Displayed"
Common errors Reference