I was recently asked to use Mercury Interactive's LoadRunner performance test tool to exercise an XML message-based server application. A common enough activity I thought, so I was surprised to find that I needed to author a little custom 'C' language coding to in order to complete the script.
In this case server exposed a standard HTTP web form to accept the XML message to process. I initially thought this would be very straightforward. I'd use LoadRunner to record an interaction, simply parameterise the resultant script, and - to vary the values in the XML message - feed the parameters from a CSV data file derived from database data.
The CSV data file looked like this:
ID,FORENAMES,SURNAME,DOB,GENDER
6774,LINDE ELENEVIE,BLACKADDER,1943-06-22,F
6303,,BOGEYMAN,1945-11-05,M
6284,FLOYD,HEALTHLOCATION,,M
6277,,FAMFAMFAM,1959-12-07,
And, when parameterised, the LoadRunner script fragment looked like this:
Action()
{
web_submit_data("EJBTest",
"Action=http://192.168.1.1/ejb-test/EJBTest ",
"Method=POST",
"RecContentType=text/xml",
"Referer=http://192.168.1.1/ejb-test/",
"Snapshot=t2.inf",
"Mode=HTML",
ITEMDATA,
"Name=xml", "Value= >{SURNAME} {FORENAMES} {DOB} {GENDER} \r\n",
ENDITEM,
LAST);
return 0;
}
Unfortunately, this script failed on the 2nd iteration, due to the 2nd record in the CSV file having no forenames. Since the "{FORENAMES}" parameter was replaced with nothing, this produced the XML message
<SearchPersons><NameSurname>BOGEYMAN</NameSurname><NameForename></NameForename><BirthDate>1945-11-05</BirthDate><GenderCode>M</GenderCode></SearchPersons>
Given that XML is already a verbose data format, standard practice has it that if the node's value is optional, the whole node is either supplied or not supplied, but never supplied with an empty value - such as happened with the "NameForename" node in the fragment above.
In our data, any combination of a CSV record's field values had the potential be empty. What we needed was for the LoadRunner script to spot this and rewrite the XML message accordingly.
Because the LoadRunner scripts produced in the 'C' programming language, we're able to augment the automatically generated script with additional custom code. In fact LoadRunner exposes a healthy library of stock utility functions to support such activities.
One such function is lr_eval_string(). This run-time function is called internally by the web_submit_data() function to parse and replace any parameters present in the argument strings with their values. Fortunately, we are also able to call this directly ourselves.
The lr_eval_string() function parses and replaces all the parameters present in the passed string. So by passing it a string with only a single parameter, it will either return the parameter's value, or an empty string. By checking the length of the returned string, we can determine of a parameterised value was present.
e.g.
if(strlen(lr_eval_string("{SURNAME}")) > 0) /* Does the replaced parameter have any length? */
{
/* A value was supplied. */
}
Using string buffers, we can use this to build a full XML message containing only XML nodes that do have values. The final code looks like this.
Action()
{
const static char sMsgPrefix[] = "Value=
const static char sMsgSuffix[] = "
char sBuffer[256] = "\000",
sParameter[256] = "\000";
strcpy(sBuffer,sMsgPrefix); /* Reset XML message construction buffer. */
strcpy(sParameter, lr_eval_string("{SURNAME}"));
if(strlen(sParameter) > 0) /* Does the replaced parameter have any length? */
{
strcat(sBuffer, "
strcat(sBuffer, sParameter);
strcat(sBuffer, "
}
strcpy(sParameter, lr_eval_string("{FORENAMES}"));
if(strlen(sParameter) > 0) /* Does the replaced parameter have any length? */
{
strcat(sBuffer, "
strcat(sBuffer, sParameter);
strcat(sBuffer, "
}
strcpy(sParameter, lr_eval_string("{DOB}"));
if(strlen(sParameter) > 0) /* Does the replaced parameter have any length? */
{
strcat(sBuffer, "
strcat(sBuffer, sParameter);
strcat(sBuffer, "
}
strcpy(sParameter, lr_eval_string("{GENDER}"));
if(strlen(sParameter) > 0) /* Does the replaced parameter have any length? */
{
strcat(sBuffer, "
strcat(sBuffer, sParameter);
strcat(sBuffer, "
}
strcat(sBuffer,sMsgSuffix); /* Add suffix to end XML message. */
web_submit_data("EJBTest",
"Action=http://192.168.1.1/ejb-test/EJBTest?cache=true",
"Method=POST",
"RecContentType=text/xml",
"Referer=http://192.168.1.1/ejb-test/",
"Snapshot=t2.inf",
"Mode=HTML",
ITEMDATA,
"Name=xml", sBuffer, ENDITEM,
LAST);
return 0;
}
The custom 'C' source code is written for clarify and no doubt optimizations exist.
Hopefully this may help others out.