Jul. 29th, 2014

How to Load Test AJAX/XHR Enabled Sites With JMeter

It’s hard to find a website or application which doesn’t use Asynchronous JavaScript And XML. Or, as you probably know it: AJAX.

 

AJAX enables the asynchronous data exchange of the client-server without interfering with the web page. It lets you update certain areas of the page without needing to reload it entirely. AJAX was developed in late 90s by Microsoft, and started getting picked up by major IT companies in the 2000s. It became the de-facto W3C standard in 2006 upon publication of the first draft of the XMLHttpRequest (XHR) documentation

 

As AJAX is so widely used, it’s important to know how to work with it. In this blog post, I’m going to show you how to use JMeter to performance test AJAX-enabled web sites.

 

Overcoming the Challenges of Using JMeter With AJAX

 

A well-known limitation of the fact that JMeter isn’t a browser is its inability to execute Javascript. This means that AJAX calls aren’t automatically executed when JMeter loads a web page. Requests can still be recorded using JMeter’s built in proxy but they’re stored as individual samplers - which is not how AJAX requests work.

 

For example, let’s say we have a Liferay Portal with the following three portlets:

 

  1. Red Rectangle = The currency converter

  2. Amber Rectangle = The RSS feed

  3. Blue Rectangle = The iFrame with BlazeMeter website

 

Now let’s see what happens when we open the Liferay home page with these three portlets. Note: I’ll be using the Firefox browser and the Firebug extension to inspect the AJAX/XHR requests.

 

 

As you can see in the above image, three XHR requests are being sent: one for the Currency Converter, another for the RSS and the final one for the Blazemeter site in iFrame.

 

We’re interested in the first two requests here as JMeter can get the content from  iFrames.

 

Go to “Retrieve All Embedded Resources from HTML Files” in HTTP Request Defaults and set it to “true”.

 

Now look at the timeline section. Here you can see that the Currency Converter and RSS requests all started at the same time and in parallel. To replicate this behaviour, you’ll need to execute the following steps:

 

  1. GET Request to /web/blazemeter/home

  2. AJAX POST request to Currency Converter

  3. AJAX POST request to RSS

 

Points two and three should be done at the same time using two threads. If the page you’re testing is sending 10 requests, you’ll need to send 10 requests in parallel using 10 threads.

 

In other words, you’ll need to execute the main GET request by one thread, followed by two POST requests by two parallel threads.

 

Up to now, JMeter hasn’t provided a sampler which can override thread group settings. So, if you want to run performance tests of AJAX-enabled pages, you have three options:  

 

  1. Use the JMeter WebDriver Sampler to measure page response times with a real browser. It can combined with a JMeter load test to monitor page response times when a web application is experiencing severe load.

  2. Add a JSR223 Sampler with custom code to kick off parallel requests to specific URLs

  3. Develop a custom Sampler capable of spawning extra threads to send requests to AJAX endpoints

 

We’ve already covered how to use the JMeter WebDriver Sampler in a previous blog post. Click here to read it now.

 

So, for the rest of this article, I’m going to focus on how you can simulate an AJAX Request with the JSR223 Sampler and develop a custom Sampler.


How to Simulate an AJAX Request with the JSR223 Sampler

 

I normally encourage JMeter and Blazemeter users to avoid scripting whenever possible. It’s better to use JMeter components and functions instead of reinventing the wheel - but sometimes scripting is a necessary evil.

 

When you literally can’t proceed without scripting, make sure you choose the most efficient way of getting things done. For something light, go with Beanshell. If your script is doing “heavy” things, opt for the JSR223 Sampler and Groovy language (take a look at the JMeter Performance and Tuning Tips guide for more info on this).

 

Groovy is the best choice for the JSR223 Sampler language because Groovy’s scripting engine has a compilable interface - meaning it will perform almost as good as a Java code. See Beanshell vs JSR223 vs Java JMeter Scripting: The Performance-Off You've Been Waiting For! for comparison benchmarks, instructions on how to install Groovy and best practices of Groovy scripting.

 

For this demo, I’m going to execute two concurrent calls to http://example.com and http://blazemeter.com endpoints.  Let’s start with the following Test Plan structure:

 

  • Test Plan

    • Thread Group (all defaults, 1 user, 1 second ramp-up, 1 loop)

      • JSR223 Sampler

    • View Results Tree Listener

 

Populate JSR223 Sampler as follows:

 

Language: Groovy

Parameters: http://example.com http://blazemeter.com

Script: See the code below:

 

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; // necessary imports

      
List<String> urls = new ArrayList<String>(); // initialize array of URLs
Collections.addAll(urls,args); // read URLs from "Parameters" input and add them to array
ExecutorService pool = Executors.newFixedThreadPool(urls.size()); // initialize pool of Future Tasks with number of threads equal to size of URLs provided
for (String url : urls) { // for each URL from list
   final String currentURL = url;
   pool.submit(new Runnable() { // Sumbit a new thread which will execute GET request

       @Override
       public void run() {
           try {
               HttpClient client = new DefaultHttpClient(); // Use Apache Commons HTTPClient to perform GET request
  HttpGet get = new HttpGet(currentURL);
     HttpResponse response = client.execute(get);
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
           } catch (Exception ex) {
               ex.printStackTrace();
           }

       }
   });
}
pool.shutdown(); // shut down thread pool
 

So, the JSR223 Sampler GUI should look like the screenshot below:

To make sure that everything will work as you expect, catch requests with the Wireshark sniffer tool.

 

As you can see, both requests start asynchronously. JMeter follows pretty much the same behaviour here as real browsers with AJAX requests. 

 

 

 

 

So, when running JSR223 Sampler AJAX request simulations, your Test Plan should look like this:

 

  • Test Plan

    • Thread Group

      • Transaction Contoller

        • Main Request

        • JSR223 Sampler

    • ….

You’ll need the Transaction Controller to get the full load time of the page and nested AJAX/XHR calls.

 

When it comes to running the JSR223 Sampler with the Groovy scripting engine in Blazemeter’s Cloud, make sure that you provide the groovy-all.jar along with your .jmx script through the File Upload dialog. Blazemeter will pick it up and make it available to all the engines on the load test.

 

How to Write a Custom AJAX Request Sampler

 

Sometimes even scripting isn’t enough to implement a missing feature. In this section, I’ll look at  the process of creating custom JMeter Samplers, taking several URLs and their requests in parallel by several threads.

 

The JMeter source is available in the Apache JMeter Downloads area. Here you’ll be able to find an ExampleSampler, showing you how the custom sampler should be implemented. I’m going to make it capable of sending HTTP requests and status reports by overriding the SampleResult sample (Entry e) method declared in Sampler interface with the code used to send parallel requests with multiple thread. This overrides the parent Thread Group limitations like I did with the JSR223 Sampler - but it also adds extra reporting and exit criteria. The project structure is relatively complex so I’ll provide it as a separate source code bundle - rather than inline it into this post.

 

Disclaimer: the example ajax-sampler source code is provided for your reference only. It’s not a fully functional sampler and doesn’t support components like the HTTP Header Manager, HTTP Authorization Manager, etc.

 

To get the AJAX Sampler:

 

  1. Fetch source or binary using any of approaches below:

  2. For “build from source” way: execute the mvn install command from ajax-sampler folder

  3. Copy the blazemeter-ajax-sampler-1.0-SNAPSHOT.jar library from ajax-sampler/target folder to the /lib/ext folder of your JMeter installation

  4. Restart JMeter

  5. Once you’ve successfully completed it, you should see an additional “Example Sampler” with this GUI:

 

 

You can use this sample to set a number of send requests to specified URLs.  The sampler will execute requests to all the specified URLs asynchronously by using an equal number of threads and URLs.

 

  • Method - HTTP Method to execute (GET, POST, PUT, etc.)

  • URL - self-explanatory, an endpoint for the request

  • Value - in case of POST, PUT, etc., the request body can be specified here

 

Now let’s go back to the Liferay scenario, which has two portlets being loaded through AJAX calls. We’ll need to have the following Test Plan Structure:

 

  • Test Plan

    • Thread Group

      • Transaction Controller

        • HTTP Request - GET request to main page

        • POST request to Currency Converter portlet

        • Post request to RSS portlet

 

The POST request details will need to be populated with captured values through Firebug or Wireshark

 

 


 

To run the test with an AJAX extension in Blazemeter’s Cloud, you’ll need to provide the following:

 

  • An AJAX.jmx test plan

  • The blazemeter-ajax-sampler-1.0-SNAPSHOT.jar extension library

 

 

Here’s what the load report for the Test Plan looks like:

 

This report reveals that the main request to the Liferay home page tool took 722 ms and the cumulative time of the execution of AJAX requests was 131 ms, 853 ms in total.

 

Take a look in the Logs section for more information on the number of URLs hit and the HTTP results received.

 

If you disable the AJAX Extension and retry the same test, you’ll see that only the main request will be executed without any XHR-generated calls and the timings are lower.   

 

To summarize, you have three options when it comes to performing application testing on AJAX powered websites. Here's a table of your options and the pros and cons of each one.

 

Approach

Pros

Cons

WebDriver Sampler

Gives a real browser experience

Requires a lot of dependency libraries. In some cases, these dependencies clash with JMeter libraries

Requires additional Selenium specific coding to wait for all AJAX events to finish

Consumes a lot of CPU/RAM 

It's recommended to limit threads to 1 only

JSR223 Sampler and groovy

Performance is very close to Java code

 

Requires the groovy library in the classpath

In case of inner classes all Java limitations apply

Custom Sampler

Native Java performance

Flexibility and extensibility

Full access to JMeter API

Requires Java development skills and extra time for implementation


 

And...that’s pretty much it on testing AJAX-enabled apps with Apache JMeter.

 

Learn more about load testing from these two free webinars:

Load Test Like a Pro

How to Create Advanced Load Testing Scenarios with JMeter


As always, I’d love to hear your comments and questions.

Interested in writing for our Blog? Send us a pitch!