Thursday, December 29, 2011

Simple LoadRunner Method to Post XML to Web Service

Here is a very simple method for posting XML to a web service from loadrunner using http protocol. This method is somewhat simpler than using the web service protocol.

XML Request In-Line
In this scenario:
  • url is saved to a parameter
  • xml request is saved to a parameter
  • http headers are defined
  • response validation is defined
  • request is sent

Here is the script:

Action()
{
    char *request_xml;


    // save web service url to param
    char *URL = "http://www.webservicex.com/globalweather.asmx";
    lr_save_string(URL, "URL_Param");


// save xml request to param
request_xml=
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
 "<soap:Body>"
  "<GetWeather xmlns=\"http://www.webserviceX.NET\">"
"<CityName>Seattle</CityName>"
"<CountryName>United States</CountryName>"
  "</GetWeather>"
 "</soap:Body>"
"</soap:Envelope>";


lr_save_string(request_xml, "REQUEST_XML_PARAM");
  
// add http headers
web_add_header("Content-Type", "text/xml; charset=utf-8");
        web_add_header("Host", "www.webservicex.com");
web_add_header("SOAPAction", "http://www.webserviceX.NET/GetWeather");


        // validate response
web_reg_find("Text=SEATTLE-TACOMA INTERNATIONAL  AIRPORT , WA, United States", LAST);


// send request
lr_start_transaction("post_xml");


web_custom_request("post_to_http_jms_provider",
 "URL={URL_Param}",
 "Method=POST",
 "TargetFrame=",
 "Resource=0",
 "Referer=",
 "Mode=HTTP",
 "Body={REQUEST_XML_PARAM}",
 LAST); 


lr_end_transaction("post_xml", LR_AUTO);
}


Sample Output
Here is the sample output:

Virtual User Script started
Starting action vuser_init.
Web Turbo Replay of LoadRunner 9.10.0 for WIN2003; WebReplay85 build 5896   [MsgId: MMSG-27143]
Run Mode: HTML   [MsgId: MMSG-26000]
Run-Time Settings file: "E:\bin\PerformanceCenter\scripts\POST_XML_TEST\\default.cfg"   [MsgId: MMSG-27141]
Ending action vuser_init.
Running Vuser...
Starting iteration 1.
Starting action Action.
Action.c(7): Notify: Saving Parameter "URL_Param = http://www.webservicex.com/globalweather.asmx"
Action.c(21): Notify: Saving Parameter "REQUEST_XML_PARAM = <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Seattle</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>"
Action.c(24): Warning -26593: The header being added may cause unpredictable results when applied to all ensuing URLs. It is added anyway   [MsgId: MWAR-26593]
Action.c(24): web_add_header("Content-Type") highest severity level was "warning"   [MsgId: MMSG-26391]
Action.c(25): Warning -26593: The header being added may cause unpredictable results when applied to all ensuing URLs. It is added anyway   [MsgId: MWAR-26593]
Action.c(25): web_add_header("Host") highest severity level was "warning"   [MsgId: MMSG-26391]
Action.c(26): web_add_header("SOAPAction") was successful   [MsgId: MMSG-26392]
Action.c(29): Registering web_reg_find was successful   [MsgId: MMSG-26390]
Action.c(32): Notify: Transaction "post_xml" started.
Action.c(34): Notify: Parameter Substitution: parameter "URL_Param" =  "http://www.webservicex.com/globalweather.asmx"
Action.c(34): Notify: Parameter Substitution: parameter "REQUEST_XML_PARAM" =  "<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Seattle</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>"
Action.c(34): t=244ms: 277-byte response headers for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=1)
Action.c(34):     HTTP/1.1 200 OK\r\n
Action.c(34):     Cache-Control: private, max-age=0\r\n
Action.c(34):     Content-Type: text/xml; charset=utf-8\r\n
Action.c(34):     Content-Encoding: gzip\r\n
Action.c(34):     Vary: Accept-Encoding\r\n
Action.c(34):     Server: Microsoft-IIS/7.0\r\n
Action.c(34):     X-AspNet-Version: 4.0.30319\r\n
Action.c(34):     X-Powered-By: ASP.NET\r\n
Action.c(34):     Date: Thu, 29 Dec 2011 22:34:56 GMT\r\n
Action.c(34):     Content-Length: 728\r\n
Action.c(34):     \r\n
Action.c(34): t=264ms: 728-byte ENCODED response body received for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=1)
Action.c(34): t=265ms: 1103-byte DECODED response body for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=1)
Action.c(34):     <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.or
Action.c(34):     g/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://
Action.c(34):     www.w3.org/2001/XMLSchema"><soap:Body><GetWeatherResponse xmlns="http://www.webserviceX.NE
Action.c(34):     T"><GetWeatherResult>&lt;?xml version="1.0" encoding="utf-16"?&gt;\r\n
Action.c(34):     &lt;CurrentWeather&gt;\r\n
Action.c(34):       &lt;Location&gt;SEATTLE-TACOMA INTERNATIONAL  AIRPORT , WA, United States (KSEA) 47-27N 
Action.c(34):     122-19W 136M&lt;/Location&gt;\r\n
Action.c(34):       &lt;Time&gt;Dec 29, 2011 - 04:53 PM EST / 2011.12.29 2153 UTC&lt;/Time&gt;\r\n
Action.c(34):       &lt;Wind&gt; from the SE (140 degrees) at 9 MPH (8 KT):0&lt;/Wind&gt;\r\n
Action.c(34):       &lt;Visibility&gt; 10 mile(s):0&lt;/Visibility&gt;\r\n
Action.c(34):       &lt;SkyConditions&gt; overcast&lt;/SkyConditions&gt;\r\n
Action.c(34):       &lt;Temperature&gt; 46.0 F (7.8 C)&lt;/Temperature&gt;\r\n
Action.c(34):       &lt;DewPoint&gt; 39.9 F (4.4 C)&lt;/DewPoint&gt;\r\n
Action.c(34):       &lt;RelativeHumidity&gt; 79%&lt;/RelativeHumidity&gt;\r\n
Action.c(34):       &lt;Pressure&gt; 29.93 in. Hg (1013 hPa)&lt;/Pressure&gt;\r\n
Action.c(34):       &lt;Status&gt;Success&lt;/Status&gt;\r\n
Action.c(34):     &lt;/CurrentWeather&gt;</GetWeatherResult></GetWeatherResponse></soap:Body></soap:Envelope
Action.c(34):     >
Action.c(34): Registered web_reg_find successful for "Text=SEATTLE-TACOMA INTERNATIONAL  AIRPORT , WA, United States" (count=1)   [MsgId: MMSG-26364]
Action.c(34): web_custom_request("post_to_http_jms_provider") was successful, 728 body bytes, 277 header bytes   [MsgId: MMSG-26386]
Action.c(44): Notify: Transaction "post_xml" ended with "Pass" status (Duration: 0.1738 Wasted Time: 0.0000).
Ending action Action.
Ending iteration 1.
Ending Vuser...
Starting action vuser_end.
Ending action vuser_end.
Vuser Terminated.



XML Request From Parameter File
The xml request message can also be saved in a parameter file rather than being defined in-line.  In this scenario:
  • url is saved to a parameter
  • xml request is saved in a parameter file which is set to update row on "sequential", update value on "once".
  • http headers are defined
  • response validation is defined
  • request is sent

Here is the script:

Action()
{
    // save web service url to parameter
    char *URL = "http://www.webservicex.com/globalweather.asmx";
    lr_save_string(URL, "URL_Param");
  
    // add http headers
    web_add_header("Content-Type", "text/xml; charset=utf-8");
    web_add_header("Host", "www.webservicex.com");
    web_add_header("SOAPAction", "http://www.webserviceX.NET/GetWeather");


    // validate response
    web_reg_find("Text=SEATTLE-TACOMA INTERNATIONAL  AIRPORT , WA, United States", LAST);


    // send request
    lr_start_transaction("post_xml");


    web_custom_request("post_xml",
 "URL={URL_Param}",
 "Method=POST",
 "TargetFrame=",
 "Resource=0",
 "Referer=",
 "Mode=HTTP",
 "Body={Request_From_File}",
 LAST); 


    lr_end_transaction("post_xml", LR_AUTO);
}



Parameter File
Here is the parameter file Requst.dat, with the xml defined on a single line, parameter properties of update row on "sequential", update value on "once":

Request
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Seattle</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>



Sample Output
Here is the sample output:

Virtual User Script started
Starting action vuser_init.
Web Turbo Replay of LoadRunner 9.10.0 for WIN2003; WebReplay85 build 5896   [MsgId: MMSG-27143]
Run Mode: HTML   [MsgId: MMSG-26000]
Run-Time Settings file: "E:\bin\PerformanceCenter\scripts\POST_XML_FROM_FILE_TEST\\default.cfg"   [MsgId: MMSG-27141]
Ending action vuser_init.
Running Vuser...
Starting iteration 1.
Starting action Action.
Action.c(6): Notify: Saving Parameter "URL_Param = http://www.webservicex.com/globalweather.asmx"
Action.c(9): Warning -26593: The header being added may cause unpredictable results when applied to all ensuing URLs. It is added anyway   [MsgId: MWAR-26593]
Action.c(9): web_add_header("Content-Type") highest severity level was "warning"   [MsgId: MMSG-26391]
Action.c(10): Warning -26593: The header being added may cause unpredictable results when applied to all ensuing URLs. It is added anyway   [MsgId: MWAR-26593]
Action.c(10): web_add_header("Host") highest severity level was "warning"   [MsgId: MMSG-26391]
Action.c(11): web_add_header("SOAPAction") was successful   [MsgId: MMSG-26392]
Action.c(14): Registering web_reg_find was successful   [MsgId: MMSG-26390]
Action.c(17): Notify: Transaction "post_xml" started.
Action.c(19): Notify: Parameter Substitution: parameter "URL_Param" =  "http://www.webservicex.com/globalweather.asmx"
Action.c(19): Notify: Parameter Substitution: parameter "Request_From_File" =  "<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Seattle</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>"
Action.c(19): t=210ms: 277-byte response headers for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=1)
Action.c(19):     HTTP/1.1 200 OK\r\n
Action.c(19):     Cache-Control: private, max-age=0\r\n
Action.c(19):     Content-Type: text/xml; charset=utf-8\r\n
Action.c(19):     Content-Encoding: gzip\r\n
Action.c(19):     Vary: Accept-Encoding\r\n
Action.c(19):     Server: Microsoft-IIS/7.0\r\n
Action.c(19):     X-AspNet-Version: 4.0.30319\r\n
Action.c(19):     X-Powered-By: ASP.NET\r\n
Action.c(19):     Date: Thu, 29 Dec 2011 22:33:25 GMT\r\n
Action.c(19):     Content-Length: 728\r\n
Action.c(19):     \r\n
Action.c(19): t=246ms: 728-byte ENCODED response body received for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=1)
Action.c(19): t=247ms: 1103-byte DECODED response body for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=1)
Action.c(19):     <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.or
Action.c(19):     g/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://
Action.c(19):     www.w3.org/2001/XMLSchema"><soap:Body><GetWeatherResponse xmlns="http://www.webserviceX.NE
Action.c(19):     T"><GetWeatherResult>&lt;?xml version="1.0" encoding="utf-16"?&gt;\r\n
Action.c(19):     &lt;CurrentWeather&gt;\r\n
Action.c(19):       &lt;Location&gt;SEATTLE-TACOMA INTERNATIONAL  AIRPORT , WA, United States (KSEA) 47-27N 
Action.c(19):     122-19W 136M&lt;/Location&gt;\r\n
Action.c(19):       &lt;Time&gt;Dec 29, 2011 - 04:53 PM EST / 2011.12.29 2153 UTC&lt;/Time&gt;\r\n
Action.c(19):       &lt;Wind&gt; from the SE (140 degrees) at 9 MPH (8 KT):0&lt;/Wind&gt;\r\n
Action.c(19):       &lt;Visibility&gt; 10 mile(s):0&lt;/Visibility&gt;\r\n
Action.c(19):       &lt;SkyConditions&gt; overcast&lt;/SkyConditions&gt;\r\n
Action.c(19):       &lt;Temperature&gt; 46.0 F (7.8 C)&lt;/Temperature&gt;\r\n
Action.c(19):       &lt;DewPoint&gt; 39.9 F (4.4 C)&lt;/DewPoint&gt;\r\n
Action.c(19):       &lt;RelativeHumidity&gt; 79%&lt;/RelativeHumidity&gt;\r\n
Action.c(19):       &lt;Pressure&gt; 29.93 in. Hg (1013 hPa)&lt;/Pressure&gt;\r\n
Action.c(19):       &lt;Status&gt;Success&lt;/Status&gt;\r\n
Action.c(19):     &lt;/CurrentWeather&gt;</GetWeatherResult></GetWeatherResponse></soap:Body></soap:Envelope
Action.c(19):     >
Action.c(19): Registered web_reg_find successful for "Text=SEATTLE-TACOMA INTERNATIONAL  AIRPORT , WA, United States" (count=1)   [MsgId: MMSG-26364]
Action.c(19): web_custom_request("post_xml") was successful, 728 body bytes, 277 header bytes   [MsgId: MMSG-26386]
Action.c(29): Notify: Transaction "post_xml" ended with "Pass" status (Duration: 0.1629 Wasted Time: 0.0000).
Ending action Action.
Ending iteration 1.
Ending Vuser...
Starting action vuser_end.
Ending action vuser_end.
Vuser Terminated.

Varying Parameters in the Request

It is also quite easy to vary the input parameters passed in the request message using the function lr_xml_set_values as follows.  In this case: 
  • the field "CityName" in the request xml is specified
  • a new value for CityName is specified
  • the resulting request xml is put in a parameter
  • the updated request is sent
 // vary parameters
lr_xml_set_values("XML=\{Request_From_File\}",
"Query=//CityName",
"Value=Spokane",
"ResultParam=XML_With_Substitution",
LAST);

// add http headers
web_add_header("Content-Type", "text/xml; charset=utf-8");
        web_add_header("Host", "www.webservicex.com");
web_add_header("SOAPAction", "http://www.webserviceX.NET/GetWeather");

// validate response
web_reg_find("Text=FELTS FIELD, WA, United States", LAST);

// send request
lr_start_transaction("post_xml");

web_custom_request("post_xml",
 "URL={URL_Param}",
 "Method=POST",
 "TargetFrame=",
 "Resource=0",
 "Referer=",
 "Mode=HTTP",
 "Body={XML_With_Substitution}",
 LAST); 

lr_end_transaction("post_xml", LR_AUTO);

The request variables can also be pulled from a parameter file as follows:
        // vary parameters
lr_xml_set_values("XML=\{Request_From_File\}",
"Query=//CityName",
"Value=\{CityNameParameter\}",
"ResultParam=XML_With_Substitution",
LAST);

Sample Output


Action.c(32): Notify: Parameter Substitution: parameter "Request_From_File" =  "<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Seattle</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>"
Action.c(32): Notify: Saving Parameter "XML_With_Substitution = <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Spokane</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>"
Action.c(32): "lr_xml_set_values" succeeded, 1 match processed
Action.c(39): Warning -26593: The header being added may cause unpredictable results when applied to all ensuing URLs. It is added anyway   [MsgId: MWAR-26593]
Action.c(39): web_add_header("Content-Type") highest severity level was "warning"   [MsgId: MMSG-26391]
Action.c(40): Warning -26593: The header being added may cause unpredictable results when applied to all ensuing URLs. It is added anyway   [MsgId: MWAR-26593]
Action.c(40): web_add_header("Host") highest severity level was "warning"   [MsgId: MMSG-26391]
Action.c(41): web_add_header("SOAPAction") was successful   [MsgId: MMSG-26392]
Action.c(44): Notify: Transaction "post_xml" started.
Action.c(46): Notify: Parameter Substitution: parameter "URL_Param" =  "http://www.webservicex.com/globalweather.asmx"
Action.c(46): Notify: Parameter Substitution: parameter "XML_With_Substitution" =  "<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><GetWeather xmlns="http://www.webserviceX.NET"><CityName>Spokane</CityName><CountryName>United States</CountryName></GetWeather></soap:Body></soap:Envelope>"
Action.c(46): t=556ms: 277-byte response headers for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=2)
Action.c(46):     HTTP/1.1 200 OK\r\n
Action.c(46):     Cache-Control: private, max-age=0\r\n
Action.c(46):     Content-Type: text/xml; charset=utf-8\r\n
Action.c(46):     Content-Encoding: gzip\r\n
Action.c(46):     Vary: Accept-Encoding\r\n
Action.c(46):     Server: Microsoft-IIS/7.0\r\n
Action.c(46):     X-AspNet-Version: 4.0.30319\r\n
Action.c(46):     X-Powered-By: ASP.NET\r\n
Action.c(46):     Date: Thu, 29 Dec 2011 22:53:27 GMT\r\n
Action.c(46):     Content-Length: 706\r\n
Action.c(46):     \r\n
Action.c(46): t=578ms: 706-byte ENCODED response body received for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=2)
Action.c(46): t=579ms: 1081-byte DECODED response body for "http://www.webservicex.com/globalweather.asmx" (RelFrameId=1, Internal ID=2)
Action.c(46):     <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.or
Action.c(46):     g/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://
Action.c(46):     www.w3.org/2001/XMLSchema"><soap:Body><GetWeatherResponse xmlns="http://www.webserviceX.NE
Action.c(46):     T"><GetWeatherResult>&lt;?xml version="1.0" encoding="utf-16"?&gt;\r\n
Action.c(46):     &lt;CurrentWeather&gt;\r\n
Action.c(46):       &lt;Location&gt;FELTS FIELD, WA, United States (KSFF) 47-41N 117-19W 609M&lt;/Location&g
Action.c(46):     t;\r\n
Action.c(46):       &lt;Time&gt;Dec 29, 2011 - 04:53 PM EST / 2011.12.29 2153 UTC&lt;/Time&gt;\r\n
Action.c(46):       &lt;Wind&gt; from the SSW (210 degrees) at 9 MPH (8 KT):0&lt;/Wind&gt;\r\n
Action.c(46):       &lt;Visibility&gt; 10 mile(s):0&lt;/Visibility&gt;\r\n
Action.c(46):       &lt;SkyConditions&gt; mostly clear&lt;/SkyConditions&gt;\r\n
Action.c(46):       &lt;Temperature&gt; 43.0 F (6.1 C)&lt;/Temperature&gt;\r\n
Action.c(46):       &lt;DewPoint&gt; 33.1 F (0.6 C)&lt;/DewPoint&gt;\r\n
Action.c(46):       &lt;RelativeHumidity&gt; 67%&lt;/RelativeHumidity&gt;\r\n
Action.c(46):       &lt;Pressure&gt; 29.99 in. Hg (1015 hPa)&lt;/Pressure&gt;\r\n
Action.c(46):       &lt;Status&gt;Success&lt;/Status&gt;\r\n
Action.c(46):     &lt;/CurrentWeather&gt;</GetWeatherResult></GetWeatherResponse></soap:Body></soap:Envelope
Action.c(46):     >
Action.c(46): web_custom_request("post_xml") was successful, 706 body bytes, 277 header bytes   [MsgId: MMSG-26386]
Action.c(56): Notify: Transaction "post_xml" ended with "Pass" status (Duration: 0.2966).
Ending action Action.
Ending iteration 1.
Ending Vuser...
Starting action vuser_end.
Ending action vuser_end.
Vuser Terminated.



Saving Parameters From XML Response
The XML response can be saved in a parameter "Response" as follows:

    // save response
    web_reg_save_param("Response", "LB=", "RB=", "Search=Body", LAST);


With the response, fields from the xml can be extracted as follows to later be used in the next xml request.  In this case field "FieldInResponseXml" is saved to parameter "ResponseFieldParam":
   lr_xml_get_values("XML=\{Response\}",
     "ValueParam=ResponseFieldParam",
     "Query=//FieldInResponseXml",
     LAST);

The parameter {ResponseFieldParam} can then be used to construct the next xml request.




Thursday, December 15, 2011

Loadrunner Technique for Handling Early Script Exits

The following shows a technique for handling the following type of scenario within a single script:
  • Percent users complete step 1: 100
  • Percent users complete step 2: 60
  • Percent users complete step 3: 10
  • etc.
In this case, all users begin the script, some of them continue on to the next part, some of them continue on to the third step, etc.  It is convenient to have this scenario handled within a single script.  Load can then be varied merely by varying the vuser count.
The user behavior controlling when they exit is handled by creating a parameter table with columns as follows
  • stepTwoPercentComplete (set to 60, e.g.)
  • stepThreePercentComplete (set to 10, e.g.)
  • etc.
The script will read the parameterized percentages, then generate a random number each iteration to determine how many steps to complete before exiting.

This technique allows a fairly complex scenario to be handled by a single simple script.

Sample Script
#include "as_web.h"
#include "lrw_custom_body.h"


char url[] = "URL=http://server001";


Action()
{   
int stepOnePercentComplete = 100; // 100% do step 1
int stepTwoPercentComplete = atoi(lr_eval_string("{stepTwoPercentComplete}")); // get param, e.g., 60% complete step 2
int stepThreePercentComplete = atoi(lr_eval_string("{stepThreePercentComplete}")); // get param, e.g., 10% complete step 3
   
  
    // Get a random percent to use in deciding whether and when to exit early in the script
    int randomPercent = (rand() % 100)+1;  // Get a random percent


lr_log_message( "--- Random Percent = %d", randomPercent );
    lr_log_message( "--- Percent users completing step 1 = %d", stepOnePercentComplete );
    lr_log_message( "--- Percent users completing step 2 = %d", stepTwoPercentComplete );
    lr_log_message( "--- Percent users completing step 3 = %d", stepThreePercentComplete );

   /****************************** STEP 1 *************************************/
   web_reg_save_param( "transactionResponse",
                       "LB=<title ID=titletext>",
                       "RB=</title>",
                       "Search=Body",
                       "NOTFOUND=Warning",
                        LAST );


   lr_start_transaction("step1");


   web_custom_request( "step 1",
                       url,
                       "Method=GET",
                       "Body=",
                       LAST );


   if( 0 != strstr( lr_eval_string( "{transactionResponse}" ), "Error" ) )  {
  lr_end_transaction( "step1", LR_FAIL );
  lr_error_message( "--- ERROR (exception found in response): %s", lr_eval_string( "{transactionResponse}" ) );
  lr_exit(LR_EXIT_ITERATION_AND_CONTINUE,LR_FAIL);
  }
   else {
  lr_end_transaction("step1", LR_PASS);
  lr_log_message( "--- Verify Response = %s", lr_eval_string( "{transactionResponse}" ));
   }
   /****************************** END STEP 1 *************************************/



   /****************************** STEP 2 *************************************/
    // DECIDE WHETHER USER EXITS BEFORE STEP 2
   if (randomPercent > stepTwoPercentComplete) {
  lr_log_message( "--- Exiting after step 1 because randomPercent > stepTwoPercentComplete (%d > %d)", randomPercent, stepTwoPercentComplete);
       lr_exit(LR_EXIT_ITERATION_AND_CONTINUE,LR_PASS);;
   }
   lr_log_message( "--- Continuing to step 2 because stepTwoPercentComplete >= randomPercent (%d >= %d)", stepTwoPercentComplete, randomPercent);


   web_reg_save_param( "transactionResponse",
                       "LB=<title ID=titletext>",
                       "RB=</title>",
                       "Search=Body",
                       "NOTFOUND=Warning",
                        LAST );


   lr_start_transaction("step2");


   web_custom_request( "step 2",
                       url,
                       "Method=GET",
                       "Body=",
                       LAST );


   if( 0 != strstr( lr_eval_string( "{transactionResponse}" ), "Error" ) )  {
  lr_end_transaction( "step2", LR_FAIL );
  lr_error_message( "--- ERROR (exception found in response): %s", lr_eval_string( "{transactionResponse}" ) );
  lr_exit(LR_EXIT_ITERATION_AND_CONTINUE,LR_FAIL);
  }
   else {
  lr_end_transaction("step2", LR_PASS);
  lr_log_message( "--- Verify Response = %s", lr_eval_string( "{transactionResponse}" ));
   }
   /****************************** END STEP 2 *************************************/




   /****************************** STEP 3 *************************************/


   // DECIDE WHETHER USER EXITS BEFORE STEP 3
   if (randomPercent > stepThreePercentComplete) {
  lr_log_message( "--- Exiting after step 2 because randomPercent > stepThreePercentComplete (%d > %d)", randomPercent, stepThreePercentComplete);
  lr_exit(LR_EXIT_ITERATION_AND_CONTINUE,LR_PASS);;
   }
   lr_log_message( "--- Continuing to step 3 because stepThreePercentComplete >= randomPercent (%d >= %d)", stepThreePercentComplete, randomPercent);


   web_reg_save_param( "transactionResponse",
                       "LB=<title ID=titletext>",
                       "RB=</title>",
                       "Search=Body",
                       "NOTFOUND=Warning",
                        LAST );


   lr_start_transaction("step3");


   web_custom_request( "step 3",
                       url,
                       "Method=GET",
                       "Body=",
                       LAST );


   if( 0 != strstr( lr_eval_string( "{transactionResponse}" ), "Error" ) )  {
  lr_end_transaction( "step3", LR_FAIL );
  lr_error_message( "--- ERROR (exception found in response): %s", lr_eval_string( "{transactionResponse}" ) );
  lr_exit(LR_EXIT_ITERATION_AND_CONTINUE,LR_FAIL);
  }
   else {
  lr_end_transaction("step3", LR_PASS);
  lr_log_message( "--- Verify Response = %s", lr_eval_string( "{transactionResponse}" ));
   }
   /****************************** END STEP 3 *************************************/
}



Thursday, December 1, 2011

How To Send Compressed XML Messages Using Loadrunner

This post describes one method to gzip XML requests prior to sending them to a web service from LoadRunner using the http protocol.  This method uses the following basic procedure:
  1. Load the zlib.dll which is included with LoadRunner.
  2. Use the gzwrite method in zlib.dll to write your xml request to a file
  3. Read the gzipped bytes into a char array
  4. Save the char array with the gzipped bytes to a parameter
  5. Set the request headers specifying compression
  6. Post the compressed request to the web service
The following script shows one example using this method:
int requestPrepared = 0;
Action()
{
typedef void *gzFile;
gzFile file;
long infile;
char *buffer;
        char *filename = "test.gz";
int count;
int fileLen;


// URL
        char *URL = (char *)"http://SERVER:8080/MyService";
        lr_save_string(URL, "URL_Param");


// .............. gzip request ..............
if ( !requestPrepared ) {
lr_load_dll("zlib.dll"); 
file = (void *)gzopen(filename, "wb");
count = strlen(lr_eval_string("{Request}"));
fileLen = gzwrite(file, lr_eval_string("{Request}"), count);
lr_log_message("Gzipped %9d bytes: ", fileLen);
gzclose(file);

// open the gzip file
infile = fopen(filename, "rb");
if (!infile) {
lr_error_message("Unable to open file %s", filename);
return;
}

// get file length
fseek(infile, 0, SEEK_END);
fileLen=ftell(infile);
fseek(infile, 0, SEEK_SET);

// Allocate memory for buffer
buffer=(char *)malloc(fileLen+1);
if (!buffer) {
  lr_error_message("Could not malloc %10d bytes", fileLen+1);
  fclose(infile);
  return;
}

// Read file contents into buffer
count = fread(buffer, 1, fileLen, infile);
lr_log_message("Read %9d bytes from gzipped file: ", count);
fclose(infile);

// Save the buffer to a loadrunner parameter
lr_save_var( buffer, count, 0, "GZippedRequest");
free(buffer);
requestPrepared = 1;
}
// .............. end gzip request ..............


// Request Headers
web_add_header("Content-Type", "application/xml; charset=UTF-8");
web_add_header("Accept-Encoding", "gzip;q=1.0, identity; q=0.5, *;q=0");
web_add_header("Content-Encoding", "gzip");
        web_add_header("Cache-Control", "no-cache");
        web_add_header("Connection", "keep-alive");


// Validate response
web_reg_find("Text=<MyNode>MyValue</MyNode>", LAST);


lr_start_transaction("Post_request");


web_custom_request("Post_Request",
 "URL={URL_Param}",
 "Method=POST",
 "TargetFrame=",
 "Resource=0",
 "Referer=",
 "Mode=HTTP",
 "Body={GZippedRequest}",
 LAST); 


lr_end_transaction("Post_request", LR_AUTO);
}

Here is step-by-step walk-through:


  • Load the zlib.dll which is included with LoadRunner.  



  • lr_load_dll("zlib.dll"); 


  • Use the gzwrite method in zlib.dll to write your xml request to a file



  • file = (void *)gzopen(filename, "wb");
    count = strlen(lr_eval_string("{Request}"));
    fileLen = gzwrite(file, lr_eval_string("{Request}"), count);


  • Read the gzipped bytes into a char array



  • infile = fopen(filename, "rb"); 
    fileLen=ftell(infile); 
    buffer=(char *)malloc(fileLen+1); 
    count = fread(buffer, 1, fileLen, infile);


  • Save the char array with the gzipped bytes to a parameter



  • lr_save_var( buffer, count, 0, "GZippedRequest");


  • Set the request headers specifying compression



  • web_add_header("Accept-Encoding", "gzip;q=1.0, identity; q=0.5, *;q=0");
    web_add_header("Content-Encoding", "gzip");


  • Post the compressed request to the web service



  • web_custom_request("Post_Request", 
      "URL={URL_Param}",
      "Method=POST", 
      "TargetFrame=",
      "Resource=0",
      "Referer=",
      "Mode=HTTP",
      "Body={GZippedRequest}",
      LAST); 

    This technique could be varied in a number of ways.  One variation would be to parameterize the gzipped file's name per vuser, so that each vuser could send a different request as follows:
         // create parameter of type "Vuser ID" using format "file-vuser%03s.gz"
        char *filename = lr_eval_string("{filename}");

    Another variation would be to create a list of different parameterized requests up front in the init() section which could then be randomly sent in the Action() section.  With this method, you would not want to go to the file system on every iteration to vary request parameters unless the expected transaction rate is low, so you would want to create the gzipped requests once up front.