Test Automation Framework (Selenium with Java) — Zeitgeist or Page Loading Strategies and Waits
S01E06 of the Test Automation Framework series about everything you’ll need to set up the nice, simple, yet sophisticated framework.
Covered with clear explanations and pretty illustrations.
Sounds like fun? Cool. Now, please, fasten your seatbelts because you’re here for a ride.
A couple of days ago I’ve received an email from the Ultimate QA, as I’ve been using one of their mock pages to fulfill a recruitment task for one of the companies I’ve been applying to a few months back.
The subject stated “Implicit vs Explicit waits??”. The email itself contained a short comparison of both Implicit and Explicit waits with three bullet points per type. There was more material on the topic available in the blog post (from 2016). It contains a decent explanation of the issues that comes with waits, but eventually, it doesn't cover much more than any other article available on the web. Nevertheless, I am grateful for that email, because it struck me with a fact that I know nothing about waits in Selenium.
Shortly after – I’ve begun my research on the topic.
First and foremost we have to understand how Selenium works in terms of loading pages. In the beginning, I went directly to the source — Selenium Docs. I will try to translate the dry citations from the docs with videos and charts so that it’s more understandable for visual learners, like myself.
I have used the tutorial for the network throttling so that we can see the difference in the loading times. In the new version of Selenium, we can directly access the Chrome DevTools using Selenium’s ChromeDriver.getDevTools() method. As you can see it uses ChromeDriver instead of WebDriver, thus we need to make changes within our code.
Source code for comparison of the PageLoadStrategy types: https://github.com/n4bik/test-automation-framework/tree/Zeitgeist-PageLoadStrategy
There are three different types of Page Loading:
This will make Selenium WebDriver wait for the entire page to be loaded. When set to normal, Selenium WebDriver waits until the load event fire is returned.
By default normal is set to a browser if none is provided.
This will make Selenium WebDriver to wait until the initial HTML document has been completely loaded and parsed, and discards loading of stylesheets, images and subframes.
When set to eager, Selenium WebDriver waits until DOMContentLoaded event fire is returned.
When set to none Selenium WebDriver only waits until the initial page is downloaded.
I sincerely hope this comparison is more than enough to get a grasp of the general concept of the PageLoadStrategy.
Once, we know how Selenium does load pages, we can move to the crème de la crème of this article — waits.
TL;DR: Use only Explicit wait (or Fluent wait, if you’re more fancy than the average).
There are four different flavors of waits to choose from:
- Thread.sleep() — causes the current thread to suspend execution for a specified period. It’s basic Java, you can learn more here at Java Docs. It’s a common mistake to use this method as it’s straightforward and works as expected — it pauses the execution of your test script for the desired amount of time. But there’s a catch — it’s not dynamic. You launch it within your test case to wait for an element and you’re going to wait for a fixed period, even if the element has been found earlier. It’s no biggie in terms of one or two different test cases, but imagine three seconds per test for over four hundred test cases. For each element. Just don’t use it in any Test Automation Framework, and you’re going to be just fine.
- ImplicitWait — this is one tough pickle. It’s unpredictable because it’s not a part of Selenium per se, but it’s implemented within each WebDriver distribution. If you don’t recall what companies are responsible for keeping each WebDriver up to date, feel free to check out the summary in one of the previous articles — S01E04 — Selenium Foundations Revisited. Long story short: every WebDriver is maintained by a different company. They don’t share knowledge and, as far as I’m concerned – there is no common standardization. Nevertheless, it may seem appealing to use the ImplicitWait as it performs wait methods before every one of the .findElement() methods without explicitly telling the WebDriver to do so. It’s neither a bad nor a good thing. Bad thing is that you have no control over the behavior of this type of wait. Also keep in mind that you should never mix Implicit and Explicit waits, as the outcome is unpredictable and you can end up with messed up Test Cases that would take more time than you’d expect or will perform unexpectedly (as the Implicit wait implementation is a subject of the changes). It’s just like with the Thread.sleep() – just don’t use it in any Test Automation Framework.
Also, here’s my sincere apologies, because BaseTest.java from my previous articles have an implementation of the ImplicitWait. Just remove the line mentioned below from the BaseTest.java and you’re ready to go. I’m going to leave a disclaimer in the previous articles, just in case. Sorry! 🙏
- ExplicitWait — just use this type of wait. It’s reliable and even though you have to explicitly (duh) set the waits in the code — using it enables you to wait for whatever state of the element that you want to wait for (e.g., visibility or invisibility of an element; element to be enabled/disabled; element to be clickable or not; etc.). Also, you can extend it and create methods that are tailored perfectly for your needs. For instance, take a look at the example from S01E05 — Page Factory and Elements Related Exceptions and see how you can create WaitForElement class and implement, for this particular example — By locator extractor to enhance your Test Automation Framework.
One more thing — ExplicitWait is actually based on the FluentWait.
- FluentWait — as you can see above, if you’re using ExplicitWait in your Test Automation Framework, you’re using FluentWait. The code line above is an excerpt from WebDriverWait.java. I’m not going to go deep on how to work with the FluentWait, as they’re not common in the real-world frameworks — at least those which I’ve been working on. Here’s a quote from the Selenium Docs, where you can also find an implementation of the FluentWait:
FluentWait instance defines the maximum amount of time to wait for a condition, as well as the frequency with which to check the condition.
Users may configure the wait to ignore specific types of exceptions whilst waiting, such as
NoSuchElementExceptionwhen searching for an element on the page.
As for final thought, I would like to present to you another comparison I’ve made to ensure that we’re not compromising anything performance-wise if we decide to go with the ExplicitWait.
Actually, we are.
Source code for comparison of the waits types: https://github.com/n4bik/test-automation-framework/tree/Zeitgeist-WaitsComparison
Nevertheless, remember that there are plenty of disadvantages of using ImplicitWait, for instance, mentioned in this thread on StackOverflow.
- Documented and defined behaviour.
- Runs in the local part of selenium (in the language of your code).
- Works on any condition you can think of.
- Returns either success or timeout error.
- Can define absence of element as success condition.
- Can customize delay between retries and exceptions to ignore.
- Undocumented and practically undefined behaviour.
- Runs in the remote part of selenium (the part controlling the browser).
- Only works on find element(s) methods.
- Returns either element found or (after timeout) not found.
- If checking for absence of element must always wait until timeout.
- Cannot be customized other than global timeout.
I’ve found an interesting thing while I was working on the comparison. I thought it’s a great opportunity to test out new Relative Locators. I tried to use them with every available wait. There are two things why I think it’s not safe to use those locators, just yet:
- If you’re going to ignore everything I wrote in this article and still use the ImplicitWait — Relative Locators are not working with it. I’ve checked it with the ImplicitWait and a mix of ImplicitWait & ExplicitWait. For the first case — tests failed each time. As for the latter one — tests passed most of the time (that’s why they’re on the comparison chart), but sometimes they would fail (it’s one of the unexpected behaviors I was writing about).
- Network Throttling or the slow connection* would cause the test to fail even if it’s using the ExplicitWait. That’s why if you'd encounter the error mentioned below — check if you’re not using Relative Locators in your code.
org.openqa.selenium.NoSuchElementException: Cannot locate an element using [unknown locator]
You can test it by yourself with the source code from here: https://github.com/n4bik/test-automation-framework/tree/Zeitgeist-WaitsComparison
*for testing purposes, to make sure that test failures are caused by the ChromeDriver DevTools use I used the Network Link Conditioner (it’s only available on macOS) with settings as presented in the picture below:
- We’ve learned about PageLoadStrategy and compared different types
- We know there are multiple different wait types, but we should use only ExplicitWait
- We know that Relative Locators are still not entirely safe to use in Test Automation Framework
In case of any questions (I believe there can be one or two of those) — feel free to post them in the comments section below.
All the best,
Tomasz Buga, SDET
PageLoadStrategy comparison GitHub Repository available at: https://github.com/n4bik/test-automation-framework/tree/Zeitgeist-PageLoadStrategy
Waits comparison GitHub Repository available at: https://github.com/n4bik/test-automation-framework/tree/Zeitgeist-WaitsComparison
All illustrations made by Tomasz Buga
Jon Snow GIF from: https://giphy.com/gifs/game-of-thrones-jon-snow-has1WKhoorwLS