Test automation methods should synchronize with the web site every time they interact with elements.
For example, before clicking an element, the test method should make sure first that the element exists and that it is clickable.
Otherwise, an exception will be generated.
The synchronization is done using explicit waits and expected conditions.
Why do we need explicit waits?
Lets take the simplest Selenium WebDriver method:
driver.findElement(locator)
How does it work?
findElement() tries finding in the browser DOM the element matched by the locator.
If the element is found, findElement() returns it.
Otherwise, findElement() fails.
findElement() works well if the website is fast.
But if the website is slow and the element is not in the browser DOM when findElement() is executed, findElement() will fail.
We need a better way of interacting with website elements.
What are Explicit Waits?
Explicit wait objects are created for the WebDriverWait class.
Each wait object has 2 parameters:
- the driver object
- a timeout
First, we create the object:
WebDriverWait wait = new WebDriverWait(driver, timeout);
Then, we tell the wait object to wait until an expected condition is reached:
- wait until the element is found and clickable
- wait until the page title is correct
- wait until the element is found and visible
- wait until the page url matches a pattern
The waiting is being done by the until() method of the WebDriverWait class.
The until() method has a parameter as well which is the expected condition to wait for.
The expected condition is created using the ExpectedConditions class:
wait.until(ExpectedConditions.condition(parameter));
To explain how an explicit wait works, I will use the following example:
//create the id locator for the searchBox element By searchBoxId = By.id("search-box"); //create the wait object WebDriverWait wait = new WebDriverWait(driver, 10); //find the searchBox element and save it in the WebElement variable WebElement searchBoxElement = wait.until( ExpectedConditions. elementToBeClickable (searchBoxId)); //type in the searchBox element searchBoxElement.click(); searchBoxElement.clear(); searchBoxElement.sendKeys("java");
What does this code work?
- the locator of the searchBox element is created
- the wait object is created with the driver object and a 10 seconds timeout as parameters.
- the waiting for the searchBox element starts:
- until() method starts a timer.
- until() method verifies if searchBox is in the browser dom and is clickable
- if the condition is true (searchBox is in the browser dom and is clickable), the waiting ends and until() method returns the searchBox element
- if the condition is not met and the timer did not reach yet the timeout value, the code waits for 500 ms before continuing from step 3.2
- if until() method finds the element, it returns the searchBox element which is saved in the WebElement variable
- the code clicks the element, clears existing value and types the keyword in it
- if until() method cannot find the searchBox element within the 10 seconds timeout, it generates an exception; the remaining code is not executed
See below a simplified diagram that explains this process:
What can we do with explicit waits?
Explicit waits can be used for:
1. finding single web element
2. finding multiple web elements
3. checking the web page title and url
4. checking the element’s status
5. interacting with frames (not included in this article)
There are a few expected conditions that can help with finding a single web element.
1. elementToBeClickable
It defines an expectation for checking that an element is visible and enabled so that you can click it.
ExpectedCondition elementToBeClickable(By locator) ExpectedCondition elementToBeClickable(WebElement element)
2. presenceOfElementLocated
It defines an expectation for checking that an element is present on the DOM of a page.
The element can be enabled or disabled.
Use this condition for checking for hidden elements.
ExpectedCondition presenceOfElementLocated(By locator)
3. visibilityOfElementLocated
ExpectedCondition visibilityOfElementLocated(By locator) ExpectedCondition visibilityOf(WebElement element)
It defines an expectation for checking that an element is present on the DOM of a page and visible.
NOTE: All following examples are complete so that you can try them by yourself.
Example
The next code sample shows how the elementToBeClickable and visibilityOfElementLocated conditions are used for finding single elements.
It uses the Vancouver Public Library website.
It automates a test case that verifies if the search component works.
import org.junit.After; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; public class TestClass { WebDriver driver; WebDriverWait wait; //this is where chromedriver.exe should be String driverPath = "c:/browserdrivers/chromedriver.exe"; @Before public void setUp() { System.setProperty("webdriver.chrome.driver", driverPath); driver = new ChromeDriver(); wait = new WebDriverWait(driver, 10); } @After public void tearDown() { driver.quit(); } @Test public void clickElementWithExplicitWait() throws InterruptedException { //open site driver.get("http://www.vpl.ca"); //finds the search box using the elementToBeClickable condition By searchBoxId = By.id("edit-search"); WebElement searchBox = wait.until( ExpectedConditions .elementToBeClickable (searchBoxId)); //types in the search box searchBox.click(); searchBox.clear(); searchBox.sendKeys("java"); //finds search button using the visibilityOfElementLocated condition By searchButtonId = By.id("edit-submit"); WebElement searchButton = wait.until( ExpectedConditions. visibilityOfElementLocated (searchButtonId)); //clicks the search button searchButton.click(); //delay so you can see whats happening in the browser Thread.sleep(5000); } }
There are a few expected conditions that can help with finding multiple web elements.
1. visibilityOfAllElementsLocatedBy
It defines an expectation for checking that all elements present on the web page that match the locator are visible.
ExpectedCondition<List> visibilityOfAllElementsLocatedBy(By locator) ExpectedCondition<List> visibilityOfAllElements(List elements)
2. presenceOfAllElementsLocatedBy
It defines an expectation for checking that there is at least 1 element present on the page.
ExpectedCondition<List> presenceOfAllElementsLocatedBy(By locator)
Example
The next code sample continues the previous one.
After the search is done, on the results page, it checks that the
- title count is 25
- author count is > 0
It finds the title and author elements using the 2 expected conditions for finding multiple elements:
import static org.junit.Assert.*; import static org.junit.Assert.*; import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; public class TestClass { WebDriver driver; WebDriverWait wait; //this is where chromedriver.exe should be String driverPath = "c:/browserdrivers/chromedriver.exe"; @Before public void setUp() { System.setProperty("webdriver.chrome.driver", driverPath); driver = new ChromeDriver(); wait = new WebDriverWait(driver, 10); } @After public void tearDown() { driver.quit(); } @Test public void clickElementWithExplicitWait() throws InterruptedException { //open site driver.get("http://www.vpl.ca"); //finds the search box By searchBoxId = By.id("edit-search"); WebElement searchBox = wait.until (ExpectedConditions .elementToBeClickable (searchBoxId)); //types in the search box searchBox.click(); searchBox.clear(); searchBox.sendKeys("java"); //finds the search button By searchButtonId = By.id("edit-submit"); WebElement searchButton = wait.until (ExpectedConditions .visibilityOfElementLocated (searchButtonId)); //clicks the search button searchButton.click(); //delay so you can see whats happening in the browser Thread.sleep(10000); //find all titles using visibilityOfAllElementsLocatedBy By titleLocator = By.xpath("//a[@testid = 'bib_link']"); List<WebElement> titles = wait.until (ExpectedConditions. visibilityOfAllElementsLocatedBy (titleLocator)); //get the number of titles found int titleCount = titles.size(); //check that the title count is equal to 25 assertEquals(titleCount, 25); System.out.println("title count = " + titleCount); //find all authors using presenceOfAllElementsLocatedBy By authorLocator = By.xpath("//a[@testid = 'author_search']"); List<WebElement> authors = wait.until( ExpectedConditions .presenceOfAllElementsLocatedBy (authorLocator)); //get the number of authors found int authorCount = authors.size(); //check that the author count is > 0 assertTrue(authorCount > 0); System.out.println("author count = " + authorCount); } }
The following expected conditions can be used for checking the web page title and url.
1. titleContains
It defines an expectation for checking that the title contains a case-sensitive substring.
ExpectedCondition titleContains(java.lang.String title)
2. titleIs
ExpectedCondition titleIs(java.lang.String title)
It defines an expectation for checking the title of a page.
3. urlContains
ExpectedCondition urlContains(java.lang.String fraction)
It defines an expectation for the URL of the current page to contain specific text.
4. urlToBe
It defines an expectation for the URL of the current page to be a specific url.
ExpectedCondition urlToBe(java.lang.String url)
5. urlMatches
ExpectedCondition urlMatches(java.lang.String regex)
It defines an expectation for the URL to match a specific regular expression
Example
Let’s improve the first code sample by checking that the page titles and urls are correct.
import static org.junit.Assert.*; import static org.junit.Assert.*; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; public class TestClass { WebDriver driver; WebDriverWait wait; //this is where chromedriver.exe should be String driverPath = "c:/browserdrivers/chromedriver.exe"; @Before public void setUp() { System.setProperty("webdriver.chrome.driver", driverPath); driver = new ChromeDriver(); wait = new WebDriverWait(driver, 10); } @After public void tearDown() { driver.quit(); } @Test public void clickElementWithExplicitWait() throws InterruptedException { //open site driver.get("http://www.vpl.ca"); //check if the home title is correct String expectedHomeTitle = "Vancouver Public Library |"; boolean isTitleCorrect = wait.until (ExpectedConditions . titleIs(expectedHomeTitle)); assertTrue(isTitleCorrect == true); //finds the search box By searchBoxId = By.id("edit-search"); WebElement searchBox = wait.until( ExpectedConditions . elementToBeClickable(searchBoxId)); //types in the search box searchBox.click(); searchBox.clear(); searchBox.sendKeys("java"); //finds the search button By searchButtonId = By.id("edit-submit"); WebElement searchButton = wait.until( ExpectedConditions . visibilityOfElementLocated (searchButtonId)); //clicks the search button searchButton.click(); //check if the results url is correct String expectedResultsUrl = "https://vpl.bibliocommons.com/search"; boolean isUrlCorrect = wait.until ( ExpectedConditions . urlContains(expectedResultsUrl)); assertTrue(isUrlCorrect == true); //delay so you can see whats happening in the browser Thread.sleep(10000); } }
All previous expected conditions are very common.
There are also other expected conditions that can be used but are less common.
No complete code samples are provided for them but feel free to try them out.
1. elementSelectionStateToBe
ExpectedCondition elementSelectionStateToBe(By locator, boolean selected) ExpectedCondition elementSelectionStateToBe(WebElement element, boolean selected)
It defines an expectation for checking if the given element is selected.
Example
WebDriverWait wait = new WebDriverWait(driver, 10); assertTrue(wait.until(ExpectedConditions. elementSelectionStateToBe(locator, true)));
2. elementToBeSelected
ExpectedCondition elementToBeSelected(By locator) ExpectedCondition elementToBeSelected(WebElement element)
It defines an expectation for checking if the given element is selected.
Example
WebDriverWait wait = new WebDriverWait(driver, 10); assertTrue(wait.until(ExpectedConditions. elementToBeSelected(locator)));
3. invisibilityOfElementLocated
ExpectedCondition invisibilityOfElementLocated(By locator)
It defines an expectation for checking that an element is either invisible or not present on the DOM.
Example
WebDriverWait wait = new WebDriverWait(driver, 10); assertTrue(wait.until(ExpectedConditions. invisibilityOfElementLocated(locator)));
4. invisibilityOfElementWithText
ExpectedCondition invisibilityOfElementWithText(By locator, java.lang.String text)
It defines an expectation for checking that an element with text is either invisible or not present on the DOM.
Example
WebDriverWait wait = new WebDriverWait(driver, 10); assertTrue(wait.until(ExpectedConditions. invisibilityOfElementWithText(locator, text)));
5. stalenessOf
ExpectedCondition stalenessOf(WebElement element)
Wait until an element is no longer attached to the DOM.
Example
WebDriverWait wait = new WebDriverWait(driver, 10); assertTrue(wait.until(ExpectedConditions. stalenessOf(element)));
6. textToBePresentInElement
ExpectedCondition textToBePresentInElement(WebElement element, java.lang.String text) ExpectedCondition textToBePresentInElementLocated(By locator, java.lang.String text)
It defines an expectation for checking if the given text is present in the element that matches the given locator.
Example
WebDriverWait wait = new WebDriverWait(driver, 10); assertTrue(wait.until(ExpectedConditions. textToBePresentInElementLocated(locator, keyword)));
7. textToBePresentInElementValue
ExpectedCondition textToBePresentInElementValue(By locator, java.lang.String text) ExpectedCondition textToBePresentInElementValue(WebElement element, java.lang.String text)
It defines an expectation for checking if the given text is present in the specified elements value attribute.
Example
WebDriverWait wait = new WebDriverWait(driver, 10); assertTrue(wait.until(ExpectedConditions. textToBePresentInElementValue(locator, keyword)));
RELATED ARTICLE
How to create custom expected conditions in Selenium
Extensive coverage of explicitly waits. It would be a great if you can take example of a site where people can practise most of these waits
LikeLike
Thanks, Paul.
I will update the article with complete code samples.
Alex
LikeLike
This is not a beginners guide but just an API reference. A beginners guide would have a full example for each and not have undefined variables or place-holders such as ‘locator’
This is the 3th site I have gone to that claims to be beginner/easy and is just a mainly a rehash of the API reference.
LikeLike
Thanks, Paul.
I will update the article with complete code samples.
Alex
LikeLike
boolean isUrlCorrect = wait.until (
ExpectedConditions .
urlContains(expectedResultsUrl));
—
This one throws me an error:
until (boolean Supplier) in ExpectedWait cannot be applied to (ExpectedConditions)
Is there something I missed?
LikeLike