AJAX Technology provides dynamic and asynchronous behavior of web pages. You can create more complex and interactive pages thanks to this technique. Developers and testers should take this into account while testing web applications.
In this article, we will look into the causes of challenges in testing such pages using Selenium cover a few options of how to make testing scripts wait until the end of asynchronous actions.
We will particularly focus on Thread.sleep(), Selenium implicit waits, and Selenium explicit waits. We will do this using the examples of the classes FluentWait and WebDriverWait. We will also provide an example using third-party library Failsafe for that purpose.
Briefly about AJAX Technology. What is the problem?
- Standards-based presentation using XHTML and CSS;
- Dynamic display and interaction using the Document Object Model;
- Data interchange and manipulation using XML and XSLT;
- Asynchronous data retrieval using XMLHttpRequest;
It is worth mentioning that AJAX Technology is using not only XML but other data presentation formats too, for example, JSON.
AJAX technology by design, allows web pages or web applications to regularly communicate with the server without reloading the page and change depending on the data received from the server.
Typically, a page sends a request to the server through the XMLHttpRequest browser interface, monitors the status of the request, and refreshes the page when the request is complete. Libraries that implement AJAX behavior, such as jQuery, also use XMLHttpRequest in them. Such libraries usually have additional options to track the request statuses.
To illustrate, we will take a small test case in which when you click the button, the page changes and the word “AJAX” appears on it.
- Given: On the page, there is a button saying “Change Content”.
- When: Press this button
- Then: An element with id=`title` and the notice `AJAX`appears on the page.
The basic code testing this case using Selenium WebDriver in Java will look as follows:
Here, before all tests, prepare WebDriver once. Before each test, the first page opens, and clickButton_axajContentLoaded script executes the described case: it presses the button and checks the notice. The examples below will illustrate how to change this code, taking into account the fact that the page behaves asynchronously.
Testing asynchronous behavior
When we check such cases using Selenium, we should take into account the fact that the notice “AJAX” will not appear on the page immediately and, most importantly, we do not know the exact moment it will appear. Thus, the test script on Selenium should wait for some elements to change instead of waiting for a new page to load. On the other hand, the tests should not last too long. Let’s see how this can be achieved.
Note that the same waiting mechanisms can be useful while testing changes on the page that go without accessing the server, for example, pages using scripts.
How to wait for changes on the page
Here is how we can implement waits: Thread.sleep(), WebDriver implicit waits, explicit waits in Selenium based on the example of WebDriverWait and FluentWait. Below we will study each of these methods separately.
Another method to implement waits is to use third party libraries that allow you to control the policies of the repetitions and catch exceptions. For example, failsafe. We will not dwell on this method but will limit ourselves to the options provided by Java and Selenium WebDriver.
Why using Thread.sleep () method is a bad idea?
The most primitive way to wait is to stop the current thread of execution by using the Thread.sleep() method.
Here the test script pauses for 10 seconds and then tries to find the desired item. Pauses greatly increase script execution time, because no matter how quickly the element appears, its search will last for a fixed period of time. In reality, we consider this rarely used method an anti-pattern.
We created one long pause and have no influence on what is happening (or rather not happening) at that moment which is a problem. It cannot in any way speed up or configure this process.
Other methods also suspend script execution, but instead of one big pause, they make small stops at certain intervals. In between pauses, you can check the status of the page and decide what to do next. Thus, the total waiting time and, consequently, the time it takes for tests to pass is reduced.
Selenium allows you to specify the implicit wait time for the driver to search for items:
The Implicit Wait does not require any action for each individual call to the findElement method. When you set the ImplicityWait value for the driver it is valid for all future requests. By default, it equals zero.
To search for an individual element, the driver polls the page until it finds the element or the specified wait interval ends. And only after that, if the element is not found, it will trigger the NoSuchElementException. If the element appears, the script immediately proceeds to the next steps. So, the sample test may take less than 10 seconds, but not longer than 10 seconds. When searching for multiple elements, the script will continue as soon as the first element appears. The advantage of using implicit waits is a concise code. We will set up the driver only once and forget about it.
Disadvantages of implicit waits
However, Selenium’s implicit waits mechanism has two significant disadvantages:
- First, this mechanism is not flexible enough, it allows you to check only one condition: whether the element appeared on the page or not. The crawling process itself is not configured in any way.
- Second, it is implemented on the remote part of Selenium (the remote part controlling the browser) and its behavior is not documented and can be vague.
As a result, it is not possible to use implicit and explicit waits at the same time, in this case, the waiting time may become uncertain, and specific bugs may arise. Large complex pages may also have performance issues. So, according to the official Selenium documentation:
“Use the ability to increase the implicit wait timeout judiciously as it will have an adverse effect on test run time, especially when used with slower location strategies like XPath.”
Answer on a related question from Jim Evans. Jim Evans is a maintainer of Selenium.
Another option is to use classes that implement the Wait interface. These waits are called explicit because they are specified each time the request is made. They may require multiple attempts. The FluentWait class allows you to configure not only the maximum search time but also the number of pauses between attempts and a set of ignored exception types. The until method described in the Wait interface takes the isTrue function as a parameter. This function repeats cyclically. An attempt fails if isTrue returns a false, null, or throws an exception as a response.
In other cases, the attempt is successful. If the attempt is successful, it returns the resulting object to the main script and the script continues.
In this example, every half second the search repeats.
- If the element is found, the script will continue immediately and the test will complete successfully.
- If an exception of the NoSuchElementException type is thrown while searching, then attempts will continue.
- If an attempt throws an exception of a different type, the script will end with that exception.
- If the element does not appear within 10 seconds, the script will end with the exception of the TimeoutException type.
The WebDriverWait class is a successor class to FluentWait that makes it more specific to be able to work with WebDriver. The two classes have the following differences:
- WebDriverWait by default ignores exceptions of the NotFoundException type (and its heirs). That is, you do not need to configure ‘ignore’ for NoSuchElementException every time.
- TimeoutException contains more information about WebDriver settings, including those for RemoteWebDriver.
There are constructors that allow you to set several parameters in one line. So, the script that performs the same actions becomes shorter:
There is a set of features that often require explicit waits. They assemble in the ExpectedConditions class.
- presenceOfElementLocated(final By locator) – the element is on the page
- visibilityOfElementLocated(final By locator) – this element is visible
- elementToBeClickable(final By locator) – You can click this element
- textToBe(final By locator, final String value) – the value `text` for the element is equal to value
Example of use:
Here instead of the function driver -> driver.findElement() use the function presenceOfElementLocated (), which performs the same action. Or we can just wait for the right text:
The principle of operation for these conditions is the same as that of the function isTrue, if you meet the condition, for example, the element is on the page and it contains a certain text, then this element returns with the function ‘until’ to the main script and the script continues.
You can configure explicit waits once and perform all operations with the same settings, for example:
This method allows you to create multiple configurations and use them for different cases. For example, increase the timeout for some operations:
Then you can use wait.until() for regular operations, and for those that are expected to take quite a long time enlargedWait.until().
One can create one’s own version of explicit waits implementing the interface Wait using a third-party library. The library Failsafe, for example, provides flexible adjusting of repetition policy, including adjustments for exceptions and conditions to pause and continue repetition cycles.
This example illustrates how to realize an explicit wait with the functionality similar to that of Selenium WebDriverWait. When we use such a wait the request will repeat 10 times through one-second pauses. The exceptions that enhance NotFoundException will be ignored. When the function isTrue returns a non-zero and non-false value, then such value will be returned to the main script, and the script will continue. Similarly, the functions of ExpectedConditions can be used here without any alterations.
Failsafe is a lightweight library with open source code and zero dependencies. You can read more about Failsafe options on the Github page.
Since it is an explicit wait too, realized on the side of the code of the tests, it is better not to be used together with implicit waits.
AJAX Technology allows you to make requests to the server and change the content of the page without reloading. If we are testing a web GUI that uses AJAX Technology, we have to consider the asynchronous behavior of the page. The Use of Thread.sleep () can greatly increase the testing time. Implicit waits work on the WebDriver side of the browser. They allow you to specify a timeout period to search for items. They work best for simple pages. Require nearly no additional code. Selenium explicit waits work on the side of the test code and are linked to a programming language. In Java, they are implemented as the classes WebDriverWait and FluentWait.
It is not recommended to use both explicit and implicit waits at the same time. This can cause unpredictable testing times and specific bugs.
The difference between FluentWait and WebDriverWait is that WebDriverWait is used specifically for WebDriver including giving more detailed error messages. ExpectedConditions contains a collection of popular conditions for implicit waits. The Wait interface can be realized by means of third-party libraries, thus enhancing the functionality of explicit waits.