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:
Red Rectangle = The currency converter
Amber Rectangle = The RSS feed
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:
GET Request to /web/blazemeter/home
AJAX POST request to Currency Converter
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:
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.
Add a JSR223 Sampler with custom code to kick off parallel requests to specific URLs
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 urls = new ArrayList(); // 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 anExampleSampler, 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 theSampleResultsample (Entry e) method declared inSamplerinterface 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:
For “build from source” way: execute the mvn install command from ajax-sampler folder
Copy the blazemeter-ajax-sampler-1.0-SNAPSHOT.jar library from ajax-sampler/target folder to the /lib/ext folder of your JMeter installation
Restart JMeter
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.
As always, I’d love to hear your comments and questions.