How to Test AJAX Requests in Selenium (Plus a Better Way)

Asynchronous JavaScript and XML, better known by its cooler acronym AJAX, is a technique for sending and receiving XMLHttpRequests between a browser and server without reloading the page. These techniques power everything from Facebook's infinite scroll news feeds to Google's dynamic ads on publisher websites.

By updating specific page elements rather than reloading the entire page, AJAX reduces load times and improves the user experience. It also conserves server resources by minimizing the amount of information transferred. The catch is it's more complex to implement and test.

Let's take a closer look at the challenges of testing AJAX applications in Selenium and some strategies that can help simplify the process.

AJAX reduces load times and improves the user experience, but it can be difficult to test in Selenium and other frameworks.

Challenges with Testing AJAX

Selenium tests are fairly straightforward: A virtual browser instance loads a page and a series of assertions are run to verify that page elements appear. Since AJAX doesn't necessarily change the page (or even the document object model (DOM)), it's hard to know when an AJAX call finishes and what was updated.

For example, suppose a user can like a post that asynchronously updates the post's like count. After a user clicks the “like” button, the like count will not immediately update since a server request must be made and the response must be processed before an update appears.

Selenium typically waits for a page to load before running assertions. But in this case, the script needs to wait until the AJAX request has finished before running the assertion. It's impossible to know exactly how long that will take, which becomes a problem for test engineers writing the script.

Techniques to Solve the Problem

The crudest way to solve the issue is using Thread.sleep() to pause a test for a set (and arbitrary) amount of time and then try to find the desired DOM element. Of course, this is an anti-pattern because overestimates can lead to increased execution time, and underestimates can lead to failing tests. 

In that light, let’s look at some better alternatives.

Implicit Waits

Using implicitlywait() specifies a certain amount of time that WebDriver should *poll* the DOM for an update. That way, there's no risk of an over- or under-estimate since there's real-time polling in place. Anything past the maximum wait time will throw an exception.

```

driver.manage().timeouts().implicitlywait(10, timeunit.seconds);

```

The tradeoff is that you can only check one condition, and the method's behavior can be unpredictable since it's based in the browser. In the official documentation, Selenium advises test engineers to use implicit waits "judiciously" as it will have an adverse effect on test runtime.

Fluent Waits

Fluent waits improves upon implicit waits by providing greater flexibility and eliminating some of the uncertainty about execution. For each instance, you can specify the frequency that conditions are checked, types of exceptions to ignore, and the maximum amount of wait time for a condition. 

```

fluentwait = new fluentwait<by>(...);

Fluentwait.pollingevery(300, timeunit.milliseconds);

fluentwait.withtimeout(1000, timeunit.milliseconds);

fluentwait.until(new predicate<by>() {

            // boolean expression

}

```

Fluent waits are best used when you want to test for the presence of an element that may appear after every X seconds or minutes. For instance, you may want to test that stock quotes update on a regular basis rather than only testing for a single instance and assuming every instance works properly.

WebDriverWait

WebDriverWait, or explicit waits, let you define certain conditions that must be present before moving on to the next part of the script. By default, WebDriverWait calls the ExpectedCondition every 500 milliseconds until it's returned successfully and the script execution progresses.

```

dynamicelement = new WebDriverWait(driver, 10)).until(ExpectedCondition.presenceOfElementLocated(...);

```

WebDriverWait is ideal if an element takes a long time to load or to check the property of an element (e.g. its presence, clickability, or other factors). Oftentimes, this is the most common technique for testing individual AJAX requests over implicit waits or timeouts.

Tracking AJAX with jQuery

The Selenium techniques discussed thus far provide enough time for AJAX calls to execute, but they don't necessarily determine when the AJAX call has finished. If the DOM doesn't change, it can be difficult for Selenium to continue the test and provide the expected results for test engineers.

jQuery is the most popular way to track AJAX calls that don't result in a DOM change. The library's `jquery.active` variable tracks the number of active AJAX calls that are in progress at any given point in time. Therefore, you can test for complete AJAX requests by simply running `jQuery.active == 0`.

The best place to incorporate the jQuery technique into a Selenium script is in the `Wait.Until` method, where you can test to ensure that the script returns as true. That way, you can be sure that the AJAX code has successfully executed and don't have to worry about failing tests.

Load Testing AJAX with LoadNinja

Load testing is another area that can be challenging for AJAX applications. Most conventional load testing platforms, such as JMeter, are protocol-based, which means they rely on servers requests and responses. Since AJAX requests don't involve page loads, tracking response times can be difficult.

LoadNinja simplifies the process by taking a browser-based approach to load testing. Using a simple record-and-replay interface, test engineers can record load tests and run them in cloud-based browser instances. These tests automatically account for AJAX responses since they're run in real browsers.

LoadNinja Extensive Analytics – Source: LoadNinja

Real browsers also make it easier to diagnose bottlenecks. In addition to detailed reporting, developers can dig into specific virtual user sessions and debug issues in the browser's DOM. LoadNinja also makes it easy to integrate load tests into existing continuous integration and deployment processes.

Sign up for a free trial of LoadNinja to see how easy it is to prevent bottlenecks in production.

Spoiler Alert: Here’s the Better Way

AJAX applications have become increasingly popular over the past decade. With the rise of React and other frameworks, test engineers have been forced to adapt to evolving requirements and cover AJAX requests and responses. The good news is that there are plenty of strategies that can help.

Including ours. In addition to integration tests, development teams should ensure that their load tests effectively cover AJAX use cases. Many protocol-based load testing frameworks can't effectively test AJAX applications, but browser-based alternatives, like LoadNinja, simplify the process.