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:
packagedev.selenium.waits;importdev.selenium.BaseTest;importjava.time.Duration;importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.Test;importorg.openqa.selenium.By;importorg.openqa.selenium.ElementNotInteractableException;importorg.openqa.selenium.NoSuchElementException;importorg.openqa.selenium.WebDriver;importorg.openqa.selenium.WebElement;importorg.openqa.selenium.chrome.ChromeOptions;importorg.openqa.selenium.support.ui.ExpectedConditions;importorg.openqa.selenium.support.ui.FluentWait;importorg.openqa.selenium.support.ui.Wait;importorg.openqa.selenium.support.ui.WebDriverWait;publicclassWaitsTestextendsBaseTest{@Testpublicvoidfails(){startChromeDriver(newChromeOptions());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"));});}@Testpublicvoidsleep()throwsInterruptedException{startChromeDriver(newChromeOptions());driver.get("https://www.selenium.dev/selenium/web/dynamic.html");driver.findElement(By.id("adder")).click();Thread.sleep(1000);WebElementadded=driver.findElement(By.id("box0"));Assertions.assertEquals("redbox",added.getDomAttribute("class"));}@Testpublicvoidimplicit(){startChromeDriver(newChromeOptions());driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(2));driver.get("https://www.selenium.dev/selenium/web/dynamic.html");driver.findElement(By.id("adder")).click();WebElementadded=driver.findElement(By.id("box0"));Assertions.assertEquals("redbox",added.getDomAttribute("class"));}@Testpublicvoidexplicit(){startChromeDriver(newChromeOptions());driver.get("https://www.selenium.dev/selenium/web/dynamic.html");WebElementrevealed=driver.findElement(By.id("revealed"));driver.findElement(By.id("reveal")).click();Wait<WebDriver>wait=newWebDriverWait(driver,Duration.ofSeconds(2));wait.until(d->revealed.isDisplayed());revealed.sendKeys("Displayed");Assertions.assertEquals("Displayed",revealed.getDomProperty("value"));}@TestpublicvoidexplicitWithOptions(){startChromeDriver(newChromeOptions());driver.get("https://www.selenium.dev/selenium/web/dynamic.html");WebElementrevealed=driver.findElement(By.id("revealed"));driver.findElement(By.id("reveal")).click();Wait<WebDriver>wait=newFluentWait<>(driver).withTimeout(Duration.ofSeconds(2)).pollingEvery(Duration.ofMillis(300)).ignoring(ElementNotInteractableException.class);wait.until(d->{revealed.sendKeys("Displayed");returntrue;});Assertions.assertEquals("Displayed",revealed.getDomProperty("value"));}@TestpublicvoidexpectedConditions(){startChromeDriver(newChromeOptions());driver.get("https://www.selenium.dev/selenium/web/web-form.html");driver.findElement(By.id("my-text-id")).sendKeys("Selenium");driver.findElement(By.cssSelector("button")).click();Wait<WebDriver>wait=newWebDriverWait(driver,Duration.ofSeconds(2));wait.until(ExpectedConditions.titleIs("Web form - target page"));WebElementmessage=wait.until(ExpectedConditions.presenceOfElementLocated(By.id("message")));wait.until(ExpectedConditions.visibilityOf(message));wait.until(ExpectedConditions.textToBePresentInElement(message,"Received!"));Assertions.assertEquals("Received!",message.getText());}}
importpytestimporttimefromselenium.commonimportNoSuchElementException,ElementNotInteractableExceptionfromselenium.webdriver.common.byimportByfromselenium.webdriver.supportimportexpected_conditionsasECfromselenium.webdriver.support.waitimportWebDriverWaitdeftest_fails(driver):driver.get('https://www.selenium.dev/selenium/web/dynamic.html')driver.find_element(By.ID,"adder").click()withpytest.raises(NoSuchElementException):driver.find_element(By.ID,'box0')deftest_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")assertadded.get_dom_attribute('class')=="redbox"deftest_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")assertadded.get_dom_attribute('class')=="redbox"deftest_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")assertrevealed.get_property("value")=="Displayed"deftest_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")orTrue)assertrevealed.get_property("value")=="Displayed"deftest_expected_conditions(driver):driver.get('https://www.selenium.dev/selenium/web/web-form.html')driver.find_element(By.ID,"my-text-id").send_keys("Selenium")driver.find_element(By.CSS_SELECTOR,"button").click()wait=WebDriverWait(driver,timeout=2)wait.until(EC.title_is("Web form - target page"))message=wait.until(EC.presence_of_element_located((By.ID,"message")))wait.until(EC.visibility_of(message))wait.until(EC.text_to_be_present_in_element((By.ID,"message"),"Received!"))assertmessage.text=="Received!"
# frozen_string_literal: truerequire'spec_helper'RSpec.describe'Waits'dolet(:driver){start_session}it'fails'dodriver.get'https://www.selenium.dev/selenium/web/dynamic.html'driver.find_element(id:'adder').clickexpect{driver.find_element(id:'box0')}.toraise_error(Selenium::WebDriver::Error::NoSuchElementError)endit'sleeps'dodriver.get'https://www.selenium.dev/selenium/web/dynamic.html'driver.find_element(id:'adder').clicksleep1added=driver.find_element(id:'box0')expect(added.dom_attribute(:class)).toeq('redbox')endit'implicit'dodriver.manage.timeouts.implicit_wait=2driver.get'https://www.selenium.dev/selenium/web/dynamic.html'driver.find_element(id:'adder').clickadded=driver.find_element(id:'box0')expect(added.dom_attribute(:class)).toeq('redbox')endit'explicit'dodriver.get'https://www.selenium.dev/selenium/web/dynamic.html'revealed=driver.find_element(id:'revealed')driver.find_element(id:'reveal').clickwait=Selenium::WebDriver::Wait.newwait.until{revealed.displayed?}revealed.send_keys('Displayed')expect(revealed.property(:value)).toeq('Displayed')endit'options with explicit'dodriver.get'https://www.selenium.dev/selenium/web/dynamic.html'revealed=driver.find_element(id:'revealed')driver.find_element(id:'reveal').clickerrors=[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)).toeq('Displayed')endend