Simple Object Access Protocol (SOAP)Web services are more commonly becoming the targets of Performance tests. This post aims to provide an introduction to testing SOAP with LoadRunner without using the Web Services VUser type. Why not use the Web Services Vuser? Errors like this
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Xml.XmlException: 'xsi' is an undeclared namespace.

Using the custom web request (as suggested by Wilson MarStuart Moncrieffand many others) bypasses the XML validation steps of LoadRunner, which is a good thing. This is possible because SOAP is just XML over HTTP, so lets take a look.

Understanding SOAP

A typical SOAP message is made up of header and body encapsulated in a SOAP envelope. The SOAP header contains application-specific information (like authentication, payment, etc) about the SOAP message. The header is optional, yet if present, must be the first child element of the Envelope. The SOAP body element contains the actual message.
SOAP EnvelopeAs SOAP uses HTTP as an application protocol, it utilises a request / response model. A SOAP request is sent to the endpoint via a HTTP POST and the returned SOAP message uses the same response codes for HTTP (200 OK, etc). This request is to get the stock price for the ticker symbol HWB (Headwired Bank), with the response returning a price of $460.75 as the last traded price.

SOAP Request

POST /StockQuote HTTP/1.1
Host: headwired.com
Content-Type: text/xml; charset="utf-8"
Content-Length: 1452
 
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<m:GetLastTradePrice xmlns:m="headwired.com">
<m:tickerSymbol>HWB</m:tickerSymbol>
</m:GetLastTradePrice>
</soapenv:Body>
</soapenv:Envelope>

SOAP Response

HTTP/1.1 200 OK
Content-Type: text/xml; charset="utf-8"
Content-Length: 1298
 
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<m:GetLastTradePriceResponse xmlns:m="Some-URI">
<m:price>460.75</m:price>
</m:GetLastTradePriceResponse>
</soapenv:Body>
</soapenv:Envelope>

SOAP as a Web Service

As a Web Service, the structure of all supported SOAP requests and responses are provided in a WSDL (Web Service Definitions Language). A WSDL provides the following information:
  • The interactions service provided
  • The arguments and results are involved in the interactions
  • The network addresses are used to locate the service
  • The communication protocol that should be used
  • The data formats that messages are represented in
In our example SOAP web have a structure for our request in the SOAP body. We have a GetLastTradePrice tag, with a child tickerSymbol . How can we determine that this is what we need to submit by looking at the WSDL? The following WSDL definition declares what elements should be in GetLastTradePrice input request.
<message name="GetLastTradePriceInput">
<part name="body" element="xsd1:TradePriceRequest"/>
</message>
This specifies that our input should contain the “ TradePriceRequest” element which is also defined in the WSDL. The definition is for one element “tickerSymbol” that must be a string value.
<element name="TradePriceRequest">
<complexType>
<all>
<element name="tickerSymbol" type="string"/>
</all>
</complexType>
</element>
The WSDL will also specify the response that we should expect from this request.
<message name="GetLastTradePriceOutput">
<part name="body" element="xsd1:TradePrice"/>
</message>
This specifies that our input should contain the “TradePrice” element which is also defined in the WSDL. The definition is for one element “price” that will contain a float value.
<element name="TradePrice">
<complexType>
<all>
<element name="price" type="float"/>
</all>
</complexType>
</element>
The WSDL below is a rough example of the stock service that would be used by the example SOAP.
<?xml version="1.0"?>
<definitions name="Stock" targetNamespace="http://headwired.com/stock.wsdl"
xmlns:tns="http://headwired.com/stock.wsdl"
xmlns:xsd1="http://headwired.com/stock.xsd"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/">
<types>
<schema targetNamespace="http://headwired.com/stock.xsd"
xmlns="http://www.w3.org/2000/10/XMLSchema">
<element name="TradePriceRequest">
<complexType>
<all><element name="tickerSymbol" type="string"/></all>
</complexType>
</element>
<element name="TradePrice">
<complexType>
<all><element name="price" type="float"/></all>
</complexType>
</element>
</schema>
</types>
<message name="GetLastTradePriceInput">
<part name="body" element="xsd1:TradePriceRequest"/>
</message>
<message name="GetLastTradePriceOutput">
<part name="body" element="xsd1:TradePrice"/>
</message>
<portType name="StockPortType">
<operation name="GetLastTradePrice">
<input message="tns:GetLastTradePriceInput"/>
<output message="tns:GetLastTradePriceOutput"/>
</operation>
</portType>
<binding name="StockSoapBinding" type="tns:StockPortType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="GetLastTradePrice">
<soap:operation soapAction="http://headwired.com/GetLastTradePrice"/>
<input><soap:body use="literal"/></input>
<output><soap:body use="literal"/></output>
</operation>
</binding>
<service name="StockService">
<documentation>My first service</documentation>
<port name="StockPort" binding="tns:StockBinding">
<soap:address location="http://headwired.com/stock"/>
</port>
</service>
</definitions>

SOAP With LoadRunner Custom Requests

SOAP in its most basic form is XML + HTTP. Therefore, we can use the LoadRunner web_custom_request function to manually specify our SOAP message. This method is more verbose than a web_service_call script, yet as it is a web request, there is no XML parsing performed by LoadRunner. This can be very useful if your web_service_call scripts are returning errors.
Scripting a web service is different a web page. Firstly, unless you have a client application, you will not be able to record and replay, instead you will have to recreate the web service calls based on logs, interface specification documents or a WSDL. It is also important to understand how the operations of the web service will be used in production.

Setup

This section discusses the steps to setup your LoadRunner script to commence scripting.

Script Type

A web_custom_request script for SOAP should be created as a “Web (HTTP/HTML)” script. This allows for Internet Protocol Run Time settings to be configured for handling authentication in a more effective way (read: web_set_sockets_option).
Web HTTP Script in LoadRunner

Authentication

The web service you are using may require HTTP level Authentication in order to access the web service operations. This can be performed using a simple web_set_user command in LoadRunner. To avoid a Authentication Error on your first request, the web_set_sockets_option can be configured along with the “Enable Integrated Authentication” run-time setting to avoid this issue.
// Requires "Enable Integrated Authentication" to be set to "yes"
web_set_sockets_option("INITIAL_BASIC_AUTH","1");
// Authenticate
web_set_user("USERNAME","PASSWORD","www.headwired.com:443");
To enable integrated authentication, open “Run-time Settings > Internet Protocol > Preferences > Set Advanced Options” and select yes for “Enable Integrated Authentication”. More information about authentication can be found here.
Enable Integrated Authentication in LoadRunner
There may also be the requirement to login using multiple authentication pairs throughout the test. This is possible through the use of the web_cleanup_cookies function. This function will remove all cookies related to the VUser, including NTLM authentication cookies.
// Clear previous logins
web_cleanup_cookies();

Scripting

Making the Request

In order to use the web_custom_request function for SOAP, we must specify that we will be submitting text/xml. The web_add_header function is used to specify this.
// Set Headers to plain text / xml
web_add_header("Content-Type", "text/xml; charset=utf8");
The web_custom_request is pretty straightforward. You specify your URL, Method and Body of the request. The SOAP message, including XML specification and Envelope is placed in the Body section of the request.
web_custom_request("Transaction_Name", 
"URL=https://www.headwired.com/StockQuote",
"Method=POST", "TargetFrame=", "Resource=0", "Referer=",
"Body=<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"
<SOAP Envelope Here>
"Snapshot=t1.inf",
LAST);

Verifying the Response

The simplest way to verify the response of a SOAP request is to use the web_reg_find function. This allows the specification of text that must be matched within the response text.
// Ensure the response contains correct text
web_reg_find("Text=GetLastTradePriceResponse",LAST);
To extract data from the response and save it as a parameter, you may also use the web_reg_save_param function. In the example below, the entire XML response, including HTTP headers is saved in the “response” variable.
// Save entire response as a string
web_reg_save_param("response","LB=","RB=",LAST);

Debugging Tips

Perhaps the easiest way to debug a web services script that utilises the web_custom_request function is to enable “Visual test results”. This feature can be enabled via the VuGen General Options, Replay Tab
Visual Test Results
The visual test results will display a report after replaying a script and provide you a script tree view, and allow you to view the XML response from the SOAP server.

Example Script

The following is a LoadRunner script that will send our sample SOAP request to the server. It will ensure that the response contains the term “GetLastTradePriceResponse”. As SOAP uses HTTP, any HTTP level errors (401, 500) will also automatically be failed by LoadRunner.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Ensure the response contains correct text
web_reg_find("Text=GetLastTradePriceResponse",LAST);
 
// Set Headers to plain text / xml
web_add_header("Content-Type", "text/xml; charset=ISO-8859-1");
 
// Submit SOAP Request
lr_start_transaction("SOAP_GetLastTradePrice");
web_custom_request("SOAP_GetLastTradePrice",
"URL=http://www.headwired.com/stock",
"Method=POST", "TargetFrame=", "Resource=0", "Referer=",
"Body=<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"
"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">"
"<soapenv:Body>"
"<m:GetLastTradePrice xmlns:m=\"Some-URI\">"
"<m:tickerSymbol>HWB</m:tickerSymbol>"
"</m:GetLastTradePrice>"
"</soapenv:Body>"
"</soapenv:Envelope>"
"Snapshot=t1.inf",
LAST);
lr_end_transaction("SOAP_GetLastTradePrice", LR_AUTO);

Post a Comment

 
Top