Overview:
One of the most common challenges with test automation is dealing with test flakiness! People who have done some automation or developers who have worked on multi-threaded application development can only understand the pain! It is painful because there are 2 different processes (test scripts in your favorite language & the browser driver) trying to talk to each other! So one might have to wait for other to be in desired state!
Usual Wait Techniques:
Page Synchronization is not very difficult as it might sound! There are many ways to handle the situations.
- Thread.sleep() – This is something like Alcohol! We know that Consuming Alcohol is injurious to health. But we still have that! Similarly we all use ‘Thread.sleep’ in our script once in a while even if we know that it is brittle and but we never accept to others we use it! It might get the job done sometimes, but affects the performance of the scripts very badly & does not produce consistent test results! [That is what flakiness is, right?]
- Implicit Wait – WebDriver is configured with specific duration for timeout to find an element. The timeout is used throughout the test script! Again, it is not a good idea & it affects the performance of the test scripts very badly!
- Explicit Wait – We use ExpectedConditions api along with WebDriverWait to make the driver wait for specific conditions like element becomes visible, expected text to appear etc! Compared to the above approaches, this is definitely better! But sometimes we might end up writing many lines of code using this lib.
- Fluent Wait
I personally use the Arquillian Graphene library which has much better synchronization methods in fluent API style – which makes the code looks neat and clean. You can check here.
If you do not want to use Arquillian for some reason, then you can take a look at this Awaitility library. Lets see how we could handle the page synchronization using this library in this article.
Using Awaitility:
- Include below dependencies in your pom file.
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility-proxy</artifactId>
<version>3.0.0</version>
</dependency>
- Add the below static imports to use the Awaitility effectively.
import static org.awaitility.Awaitility.*;
import static org.awaitility.Duration.*;
import static java.util.concurrent.TimeUnit.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
Awaitility with WebDriver:
- Waiting for specific text to appear. In the below example – we are waiting for the File download progress bar to show the Complete! message.
driver.get("http://www.seleniumeasy.com/test/jquery-download-progress-bar-demo.html");
driver.findElement(By.id("downloadButton")).click();
WebElement progress= driver.findElement(By.cssSelector("div.progress-label"));
// Wait for the download to complete - max 20 seconds
await("Download did not complete within 20 seconds").atMost(20, TimeUnit.SECONDS)
.until(progress::getText, is("Complete!"));
//Did you notice? The robust wait statement is just an one-liner! It looks neat and clean!
System.out.println("DONE!!");
- Waiting for AJAX request to complete / ‘loading’ message to disappear. In the below example, whenever you click on the ‘Get New User’ an AJAX rquest is made to get a new user information. A loading message is shown as shown here till the request is complete.
driver.get("http://www.seleniumeasy.com/test/dynamic-data-loading-demo.html");
WebElement newUserBtn = driver.findElement(By.id("save"));
WebElement loadingElement = driver.findElement(By.id("loading"));
// Get a new User
newUserBtn.click();
//Wait for the loading to disappear
await("Wait for new user to load").atMost(5, SECONDS)
.until(loadingElement::getText, not("loading..."));
- I clicked on the ‘Get New User’ 20 times in a loop – It worked like charm.
for(int i=0; i<20; i++){
newUserBtn.click();
await("Wait for new user to load").atMost(5, SECONDS)
.until(loadingElement::getText, not("loading..."));
}
- Handling Timing Alert and Exceptions: In the below example, we wait for the alert to appear – when we try to switch to alert when it is not present, it throws an exception – we handle that using ‘ignoreExceptions()’
driver.get("http://www.seleniumframework.com/Practiceform/");
WebElement alertButton = driver.findElement(By.id("timingAlert"));
//To check if alert is present - By the way, predicates are cool!
Predicate<WebDriver> isAlertPresent = (d) -> {
d.switchTo().alert();
return true;
};
//click - alert appears after 3 seconds
alertButton.click();
//wait for 5 seconds - ignore alert not present exception
await("Wait for alert").atMost(5, SECONDS)
.ignoreExceptions()
.until(() -> isAlertPresent.test(driver));
driver.switchTo().alert().accept();
- At Least – Lets say we have a requirement to show the alert only after 2 seconds – then the test should fail if it appears before that. Awaitility can verify that too!
//alert should not appear within 2 seconds - max 5 seconds - ignore alert not present exception
await("Wait for alert").atLeast(2, SECONDS)
.and()
.atMost(5, SECONDS)
.ignoreExceptions()
.until(() -> isAlertPresent.test(driver));
Summary:
Awaitility is a cool library with a lot of features. This was a quick tutorial. I might not be able to cover all the possible combinations in this. But hopefully you got the idea! I would suggest you to read more on this wiki. You could also combine this lib with ExpectedConditions API to write a better readable code.
All the best for you have to robust test suite with Awaitlity!
Happy Testing & Subscribe