Load testing of a REST API, using HP LoadRunner’s web_custom_request

This post was written by Boris Kozorovitzky, from the LoadRunner R&D Team

 

In recent years, Representational State Transfer (REST) has become a dominant model for software architecture, especially for Web-based applications. Today, most large websites and Web applications on the Internet have REST APIs. Some examples of websites using REST APIs include Twitter, Google, Flickr and so on.

 

The great thing about a REST API is that it is usually well-defined and therefore allows clear separation between the client logic and the server logic. This property in itself is very beneficial for both functional and load testing of the application server. In the following article we will demonstrate an easy way to test your application’s REST API using HP LoadRunner’s web_custom_request method.

 

If you haven't experienced HP LoadRunner, here is your opportunity to download it.

 

REST API Basics

Before we dive into load testing, let’s look at the basic terminology of a REST API. A REST API call consists of three major components:

  1. The Uniform Resource Identifier (URI) – This is a string which consists of three parts. The first part contains the host (and optionally, the port) of the API call (e.g. http://www.hp.com). The second part contains the path which identifies the call functionality. This path can include some parameter values although they are distinguishable from the constants only by semantics (e.g. /users/megwhitman/type/ceo, where “megwhitman” and “ceo” are parameters while “users” and “type” are constants). The third part is the QueryString which is a list of key-value pairs (e.g. user= megwhitman & type=ceo). The pairs are separated by the “&” sign and the entire QueryString is separated from the path by the “?” sign. A full URI may look something like this: http://www.shopping.hp.com/en_US/home-office/-/products/Tablets/Tablets?SearchParameter=ElitePad
  2. The verb – The verb can be any word describing the operation you want to perform on the URI. In HTTP, the five most commonly used verbs are: GET, POST, PUT, DELETE, and PATCH. Note that the operation for each verb is determined by the server although there are some common conventions in this area.
  3. The payload (data) – This is the data sent to the server with the request. For example, if we want to save a text file to the server, we can send the content of the text file as the payload.

 

Test Case

The example application under test is a todoMVC application which manages a ‘todo’ list.  This application has a REST API, as follows:

  • /todos, GET, No Payload – Retrieves all the todos currently in the list
  • /todos, POST, { isFinished :<isFinished>, todoText: <text>} – Creates a new todo on the server with the text provided in the payload by the <text> parameter.
  • /todos/<id>, PUT,  {id:<id>, todoText: <text>, isFinished:< isFinished>} – Updates the todo with id <id> with the text <text> and finished state < isFinished>

Note that all the data passed to and received from the server is in the JavaScript Object Notation (JSON) format which is gradually becoming the standard format for data transfer.

 

HP LoadRunner allows us to easily simulate HTTP calls through the web_custom_request function. We want to simulate a business process as follows:

1)      Get the existing todos from the server

2)      Add two new todos

3)      Update the last todo added with a new text

 

Step 1

We use recording to ‘learn’ this step. In VuGen we create a new Web - HTTP/HTML script and record a browser opening the todoMVC application. When the application starts, it gets all the todos currently stored on the server through the GET API call. We can easily find the correct step in the recorded script by looking at the URL argument. In this case the argument is - "URL=http://localhost:3000/todos" (since the application is running locally on port 3000 on my machine). The relevant step is:

 

    web_custom_request("todos"
        "URL=http://localhost:3000/todos"
        "Method=GET"
        "Resource=1"
        "RecContentType=application/json"
        "Referer=http://localhost:3000/"
        "Snapshot=t66.inf"
        LAST);

 

The “Method” argument is the REST verb (GET in this case), and the “Snapshot” argument links to the correct snapshot file on the disk. The snapshot of this step can be viewed in the Snapshot Viewer:

 

snapshots.png

 

We can see that the response from the server is a list of all the todos presented by our application in JSON format. 

 

Step 2

Adding a new todo is very simple. We create our own web_custom_request step with the parameters required by the POST  REST API. The created step looks something like this:

 

        web_custom_request("Add Todo"
        "URL=http://localhost:3000/todos"
        "Method=POST"
        "Resource=0"
        "EncType=application/json"
        "Mode=HTTP",
        "Body={\"isFinished\":false,\"todoText\":\"This is VuGen\"}",
        LAST);

 

A few things to notice here:

1)      The “Method” (verb) is POST as required by the REST API.

2)      We specified the “EncType” parameter to inform the server that the payload is going to be in JSON format.

3)      We added our payload to the “Body” parameter in JSON format. 

 

Since writing the JSON in the middle of the script may be a bit cumbersome, we can add it as an extra file to the script. To do this simply right-click the “Extra Files” node in the Solution Explorer and select “Add Files to Script…”, and choose the data.json file we prepared earlier.  On a fresh installation of LoadRunner you may need to specify the .json extension as a valid extension for extra files. You can also do this after adding the file to your script. In VuGen, select Tools > Options > Scripting > Script Management and add the .json extension to the Allowed Extensions list. Once the file is added, the project looks something like this:

 

json.jpg

 

Now we can add the file as our payload by specifying the BodyFilePath parameter:

 

        web_custom_request("Add Todo from file"
        "URL=http://localhost:3000/todos", 
        "Method=POST"
        "Resource=0"
        "EncType=application/json"
        "Mode=HTTP",
        "BodyFilePath=data.json",
        LAST);

 

Step 3

We want to update the last todo with some new values. To do this, we use another web_custom_request with the PUT verb as specified by the REST API. This is very similar to the step that adds a new todo:

 

    web_custom_request("Update Todo"
        "URL=http://localhost:3000/todos/3"
        "Method=PUT"
        "Resource=0"
        "EncType=application/json"
        "Mode=HTTP",
        "Body={\"id\":3, \"isFinished\":false,\"todoText\":\"Updated the name\"}",
        LAST);

 

This command updates the todo with id = 3 with the string “Updated the name”.

As with the previous step, you can manage the JSON text as a file, rather than writing it directly into the script.

 

 

Conclusion

We’ve seen how easy it is to create a simple load test with a REST API using HP LoadRunner.  The web_custom_request function is versatile enough to cover both simple and complex cases, thus simplifying REST API testing.

 

 

Leave us a comment in the box below to let us know how you’re using LoadRunner to load test your REST APIs.

 

 

Thanks to Boris for providing this article!

 

Click here to learn more about HP LoadRunner.

You can also download HP LoadRunner here.

 

DownloadWP-A Closer look at HPLoadRunner.png

 

Comments
NaveenKumar N(anon) | ‎08-21-2013 05:44 AM

Very insight article.

NaveenKumar N(anon) | ‎08-21-2013 11:18 AM

How to install/host TODOMVC in my laptop? Please guide me. Thanks!

HP Expert | ‎08-22-2013 12:15 AM

Hi,

 

I will ask Malcolm to attach the implementation for the todoMVC application I talk about in the blog post.

To install it you need to have node.js installed. Once installed extract the attached zip file to some directory. I will refer to this directory as "c:\todoMVC" but you can extract to any directory.

Open the node.js command prompt (Start -> All Programs -> node.js -> node.js command prompt).

A cmd window will open. In the opened cmd window go to the directory where you extracted the zip file (e.g. "cd c:\todoMVC"). Type "npm install" (without the quotes) to download all the dependencies. If you are behind a proxy you will need to specify it using the --proxy flag (e.g. "npm install --proxy http://web-proxy.hst.hp.com:8080").

Run the application server by typing "node app.js". You should see the text: "Listening on port 3000" appear in the console.

Open your favorite browser (please don't use IE)  and type "localhost:3000" in the address bar. You should see the application running.

To reset the application go back to the cmd window and press Control+C to stop it, then type "node app.js" to start it. Note that closing the browser does not restart the application as it runs in the cmd window and the browser is used to access it.

 

Good Luck,

Boris.

 

 

| ‎08-22-2013 12:24 AM

Thanks _Buzzy.  I've attached the file to the blog, as todo_mvc_example.zip

NaveenKumar N(anon) | ‎08-23-2013 09:58 PM

I installed node.js and the I tried above steps. But I am not able to navigate to C:\todo_mvc_example  where I extracted the example. 

 

 

Please see this screenshot http://prntscr.com/1n2jrq

 

Could you please help me out? Thanks!

HP Expert | ‎08-26-2013 12:02 AM

Hi NaveenKumar,

 

I think you ran "node.js" instead of the "node.js command prompt". 

Please follow the instructions I posted.

 

Thanks,

Boris.

SDMike | ‎09-03-2013 02:55 PM

A timely article in my case.  I recently did this for an internal project.  I hadn't considered adding the body payload as a JSON file though and that may simplify things for some of my requests.

| ‎09-04-2013 12:19 AM

That's great news, SDMike.  Thanks for the feedback!

Bhaskar Venkataramana(anon) | ‎10-23-2013 03:11 PM

but when we get the response if there is any "&" char than loadrunner will capture as "&amp", how do we resolve this issues

HP Expert | ‎10-24-2013 04:53 AM

Hi,

 

Could you please describe the problem in detail. I didn't understand what the issue is.

Bhaskar Venkataramana(anon) | ‎10-24-2013 06:22 AM

can you help in Search and replace a character in a string

 

for eg:

web_reg_save_param("IDValues1","Notfound=warning" ,"LB=24skiptoken""RB=\"/></feed>""Ord=1","IgnoreRedirections=Yes"LAST);

 

the IDValues1 = "&amp%24skiptoken=hNhpItje6%2BpyE5PPcO9pOz0a%3B1000"

 

now i need to replace "&amp" with "&"

HP Expert | ‎10-24-2013 06:56 AM

Hi,

 

It is hard to help you since you are not giving any context of the problem and I don't understand what you are trying to do and how.

 

From the two lines you posted it seems that you need HTML decode the string and not search and replace.

 

Please ask scripting questions at our support forum here:

http://h30499.www3.hp.com/t5/LoadRunner-Support-Forum/bd-p/sws-LoadRunner_SF

 

Thanks,

Boris.

JoeRohan | ‎02-11-2014 02:13 PM

I tried this tecnique, but not geting a response on the POST. To validate that the GET is requesting and responding I'm looking in the Tree and then click on the HTTP view. I see a request and response. Very nice.

However when I try to validate a POST, I don't see a request or result in the Tree view. I tried putting a snapshot in the script, no results. Something else, the first time I ran this script today, I did get a result in the run-time viewer, but every run since then I never see a result. In the Replay log I see that the Action.c(38): web_custom_request("web_custom_request") was successful. But how do I know it was successful? I don't see a request or response. How do I know if the POST worked?

HP Expert | ‎02-12-2014 12:56 AM

Hi Joe,

 

From your comment I understand you are using an older version of LR (11.0X). I tested my script with LR 11.52 although it should theoretically work with 11.0X.

 

You should see a response from the server in the snapshot RAW view. The response should be 200 OK.

If you don't see a snapshot at all, make sure you followed the steps correctly and that you have all the fields defined (especially Resource=0).

 

Hope this helps,

Boris.

JoeRohan | ‎02-12-2014 09:24 AM

I'm doing something similar to step 1 and step 2 in the example. I see raw response data for step one or the first web_custom_request (the GET), but no raw data for step 2 (the POST).  I am using Resource=0, here is the code.

I'm also doing EncType=application/xstream instead of JSON, thanks for your help.

 

Action()
{
    web_set_user("xxx12345",
        lr_decrypt("xxxx"),
        "xx.moo.com:xx");

    web_custom_request("gateway",
        "URL=https://xx.moo.com/GAP_WEB/rest/gateway",
        "Method=GET",
        "Resource=1",
        "RecContentType=text/plain",
        "Referer=",
        "Snapshot=t1.inf",
        LAST);

    web_custom_request("web_custom_request",
        "URL=https://xxxxxx.moo.xxx/GAP_WEB/rest/gateway/SearchGroupEvent",
        "Method=POST",
        "TargetFrame=",
        "Resource=0",
        "Referer=",
        "Mode=HTTP",
/*       "Snapshot=t2.inf",  */
       "EncType=application/xstream",
       "Body=<com.moo.gfs.gap.common.event.SearchGroupEvent>"
         "<profilingId>1389375050239</profilingId>"
         "<attributes>"
           "<entry>"
             "<string>operator_id</string>"
             "<string>xxx84470</string>"
           "</entry>"
         "</attributes>"
         "<validationMessages/>"
         "<warningMessages/>"
         "<warnOverride>false</warnOverride>"
         "<executionTime>0</executionTime>"
         "<searchRequest>"
           "<comments/>"
           "<validateUIErrors/>"
           "<trimmed>false</trimmed>"
           "<loadMethod>2</loadMethod>"
           "<groupName>Hall Test*</groupName>"
           "<city></city>"
           "<state></state>"
           "<zipCode></zipCode>"
           "<quoteNumber></quoteNumber>"
           "<groupExternalId></groupExternalId>"
         "</searchRequest>"
         "<isMaxResultReturned>false</isMaxResultReturned>"
       "</com.mutualofomaha.gfs.gap.common.event.SearchGroupEvent>",
               LAST);
    return 0;
}


HP Expert | ‎02-13-2014 12:24 AM

Hi,

 

It is hard to "debug" your script over a blog post comments. I don't see any problem with the script so there must be something specific to the application you are testing. Try to tweak it around some more and I am sure you will find the problem.

 

You can always open a support ticket and get one of our professional support people to help you with the script.

 

Thanks,

Boris.

Leave a Comment

We encourage you to share your comments on this post. Comments are moderated and will be reviewed
and posted as promptly as possible during regular business hours

To ensure your comment is published, be sure to follow the Community Guidelines.

Be sure to enter a unique name. You can't reuse a name that's already in use.
Be sure to enter a unique email address. You can't reuse an email address that's already in use.
Type the characters you see in the picture above.Type the words you hear.
Search
About the Author
Malcolm is a functional architect, focusing on best practices and methodologies across the software development lifecycle.


Follow Us
The opinions expressed above are the personal opinions of the authors, not of HP. By using this site, you accept the Terms of Use and Rules of Participation