LoadRunner VuGen: Build Your Own web_custom_request
Posted on Jun, 2012 by Admin
Guest article from Tony Gartrell.
There are occasions where a dynamic web_submit_data function needs to be built pro grammatically. Sometimes it is possible to work around them by only sending the information that has changed since the last form submit. But there are times where this is not be possible. For example, take the “cancel itinerary” step in the HP Web Tour test application (that comes with LoadRunner) when recorded in Explicit URL or URL mode. You get a request like this:
This list will change as more flights are added or canceled. More named value pairs will accumulate and you don’t always know how many will be encountered. What is a scripter to do? There have been a number of different approaches to this on various sites, as searching the web will reveal. One option is to create an array of name and value. This has some limitations. What if you had 500 flights? How big is too big for an array? The other option is to use a web_custom_request and build the body out manually. This could be done with any number of C functions (such as strcat and sprintf). Just make sure to handle memory allocation correctly. I try and stay away from using C code when possible so I don’t have to deal with memory allocation errors. I have chosen to use some of new LoadRunner functions that were added in version 11: lr_param_sprintf and lr_paramarr_idx.
To solve our issue on the web tours application, we’ll need to figure out the format of the web_custom_request. This can be done by regenerating the script using the Recording Option URL-based script with the URL Advanced option “Use web_custom_request only.” This should present a body statement that looks like this:
I like to make this a bit more read able by adding a carriage return after the ampersand. This makes it look like this:
It looks similar to web_submit_data now. Instead of having ITEMDATA with Name, Value, and ENDITEM – the Name and Values are separated by an ampersand. The order of items in web_submit_data or web_custom_request doesn’t really matter. This helps reduce the coding needed as demonstrated below.
First, we need to capture the values to be used. This is done with the standard correlation function, web_reg_save_param on the previous page using the ORD=ALL argument.
//<input type="hidden" name="flightID" value="14098-820-JM" />
web_reg_save_param("c_flightids",
"lb=<input type=\"hidden\" name=\"flightID\" value=\"", "rb=\" />",
"ord=all",
LAST);
//<input type="hidden" name=".cgifields" value="6" />
web_reg_save_param("c_cgifields",
"lb=<input type=\"hidden\" name=\".cgifields\" value=\"",
"rb=\" />",
"ord=all",
LAST);
web_url("Itinerary Button",
"URL=http://127.0.0.1:1080/WebTours/welcome.pl?page=itinerary",
"TargetFrame=body",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home",
"Snapshot=t3.inf",
"Mode=HTML",
LAST);
Start building the web_custom_request from this. Typically, you’ll want delete the last value added. This can be easily done with code below:
// This code selects what itinerary to cancel.
// In this case it is the last one that was just created.
lr_param_sprintf("c_buffer",
"%s=on&",
lr_eval_string("{c_flightids_count}"));
With the lr_param_sprintf function, you can easily format strings and send multiple variables to it. In this case, we’re sending the value of the c_flightsids_count, which is the count of the number of flights. %s is replaced with this value, so if you had six flights, “6=on” would be the value of c_buffer.
If you wanted to delete the first x number of itineraries, something like this might work:
// This code can be used if you want to delete from the top of the
// list x number of itineraries.
lr_save_string("", "c_buffer");
for (i=1;i<=2;i++)
{
lr_param_sprintf("c_buffer",
"%s%d=on&",
lr_eval_string("{c_buffer}"), i);
}
The complex part is building the flight id and cgi field requests dynamically based on how many named value pairs there are.
//This code builds the values for flight id and cgi fields
for (i=1;i<=atoi(lr_eval_string("{c_flightids_count}"));i++)
{
lr_param_sprintf("c_buffer", "%sflightID=%s&",
lr_eval_string("{c_buffer}"),
lr_paramarr_idx("c_flightids",
i));
lr_param_sprintf("c_buffer",
"%s.cgifields=%s&",
lr_eval_string("{c_buffer}"),
lr_paramarr_idx("c_cgifields", i));
}
The code will need to loop based on the number of flights there are currently. This can be done using the c_flightsids_count value again with a FOR loop. The c_buffer parameter. will also need to be added by making the first format value %s equal to the c_ buffer value. Add the format needed for flightID= along with the second formatted value %s to the value of the current c_flightids, followed by the ampersand. Now use lr_paramarr_idx based on where we are in the for loop (a great use of this function). The code will cycle through both the values of c_flightids and c_cgifields automatically. Since the order of the values do not matter, both flightID and .cgifields can be built at the same time – even though they were separated in the original request.
Add the last remaining part of the request to the buffer:
// This line adds the suffix to the end of the request
// and saves it as the c_wcr parameter
lr_save_string(lr_eval_string("{c_buffer}removeFlights.x=36&removeFlights.y=4"),
"c_wcr");
The full body of the web_custom_request has been saved as parameter c_wcr. The newly created web_custom_request is substituted for the web_submit_data.
//Here is the custom built web_custom_request
web_custom_request("itinerary.pl_2",
"URL=http://127.0.0.1:1080/WebTours/itinerary.pl",
"Method=POST",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/itinerary.pl",
"Snapshot=t23.inf",
"Mode=HTTP",
"Body={c_wcr}",
LAST);
The great thing about this is that it was all done without using a single C function. We only needed a FOR loop, and declared only one variable (i) for the for loop.
Here is the script in its entirety:
Action()
{
int i;
//<input type=hidden name=userSession value=108519.968084245ffiVQQfpifiDDDDDDDtQVpQHfVf>
web_reg_save_param("c_session",
"lb=<input type=hidden name=userSession value=",
"rb=>",
LAST);
lr_start_transaction("HomePage");
web_url("WebTours",
"URL=http://127.0.0.1:1080/WebTours/",
"TargetFrame=",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTML",
LAST);
lr_end_transaction("HomePage", LR_AUTO);
lr_start_transaction("LogOn");
web_submit_data("login.pl",
"Action=http://127.0.0.1:1080/WebTours/login.pl",
"Method=POST",
"TargetFrame=body",
"RecContentType=text/html",
"Referer=http://127.0.0.`:1080/WebTours/nav.pl?in=home",
"Snapshot=t2.inf",
"Mode=HTML",
ITEMDATA,
"Name=userSession", "Value={c_session}", ENDITEM,
"Name=username", "Value=jojo", ENDITEM,
"Name=password", "Value=bean", ENDITEM,
"Name=JSFormSubmit", "Value=off", ENDITEM,
"Name=login.x", "Value=0", ENDITEM,
"Name=login.y", "Value=0", ENDITEM,
LAST);
lr_end_transaction("LogOn", LR_AUTO);
lr_start_transaction("Itineraries");
//First, grab the parameter values for
//flight id and cgi fields using ord=all
//<input type="hidden" name="flightID" value="14098-820-JM" />
web_reg_save_param("c_flightids",
"lb=<input type=\"hidden\" name=\"flightID\" value=\"",
"rb=\" />",
"ord=all",
LAST);
//<input type="hidden" name=".cgifields" value="6" />
web_reg_save_param("c_cgifields",
"lb=<input type=\"hidden\" name=\".cgifields\" value=\"",
"rb=\" />",
"ord=all",
LAST);
web_url("Itinerary Button",
"URL=http://127.0.0.1:1080/WebTours/welcome.pl?page=itinerary",
"TargetFrame=body",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/nav.pl?page=menu&in=home",
"Snapshot=t3.inf",
"Mode=HTML",
LAST);
lr_end_transaction("Itineraries", LR_AUTO);
//This code can be used if you want to delete
//from the top of the list x number of itineraries.
lr_save_string("", "c_buffer");
for (i=1;i<=2;i++)
{
lr_param_sprintf("c_buffer", "%s%d=on&", lr_eval_string("{c_buffer}"), i);
}
//This code selects what itinerary to cancel.
//In this case it is the last one that was just created
lr_param_sprintf("c_buffer",
"%s=on&",
lr_eval_string("{c_flightids_count}"));
//This code builds the values for flight id and cgi fields
for (i=1;i<=atoi(lr_eval_string("{c_flightids_count}"));i++)
{
lr_param_sprintf("c_buffer",
"%sflightID=%s&",
lr_eval_string("{c_buffer}"),
lr_paramarr_idx("c_flightids",
i));
lr_param_sprintf("c_buffer",
"%s.cgifields=%s&",
lr_eval_string("{c_buffer}"),
lr_paramarr_idx("c_cgifields",
i));
}
//This line adds the suffix to the end of the request
//and saves it as the c_wcr parameter
lr_save_string(lr_eval_string("{c_buffer}removeFlights.x=36&removeFlights.y=4"), "c_wcr");
lr_start_transaction("CancelItinerary");
//This is a validation to make sure that
//the last flight on the list was canceled
lr_save_string(lr_eval_string(lr_eval_string("{c_flightids_{c_flightids_count}}")),
"c_cancelflight");
web_reg_find("Text={c_cancelflight}", "Fail=Found", LAST);
//Here is the custome built web_custom_request
web_custom_request("itinerary.pl_2",
"URL=http://127.0.0.1:1080/WebTours/itinerary.pl",
"Method=POST",
"Resource=0",
"RecContentType=text/html",
"Referer=http://127.0.0.1:1080/WebTours/itinerary.pl",
"Snapshot=t23.inf",
"Mode=HTTP",
"Body={c_wcr}",
LAST);
lr_end_transaction("CancelItinerary", LR_AUTO);
return 0;
}
As always – your comments are welcome.