Blogs Hub

by AskGif | Jun 25, 2019 | Category :coding

Testing Webservice with JMeter: Passing data from one response to another request

Testing Webservice with JMeter: Passing data from one response to another request

<p>JMeter is great for functional and performance testing of many things, including web services (and to my surprise also LDAP). It also provides means for extracting data from a response and passing them to a subsequent request, which is exactly what I needed.&nbsp;</p> <p>&nbsp;</p> <p>The steps are:</p> <p>Create a web service (WS) test plan, as described in the tutorial (in my case it contains two WS calls)</p> <p>Add the User Defined Variables config element to the test plan and define there a variable for transferring the response data</p> <p>Add an XPath Extractor Post Processor to the first WS call to extract the value of interest into the user-defined variable (beware namespaces!)</p> <p>Add a BeanShell PreProcessor to the second call, which will replace a placeholder in the WS call's XML data with the value of that variable</p> <p>&nbsp;</p> <p>About the web service</p> <p>I needed to test a web service, which requires its client to call first its authenticate method, which returns an authentication token called 'certificate', which is then used in subsequent requests.</p> <p>&nbsp;</p> <p>A basic implementation</p> <p>&nbsp;</p> <p>0. Setup</p> <p>Download JMeter 2.3.4 and two dependencies, Java Mail API (mail.jar) and JavaBeans Activation Framework (activation.jar), necessary for the JMeter's web service sampler. Put the JARs in JMeter's lib/ folder.</p> <p>1. Create a web service (WS) test plan, as described in the tutorial (in my case it contains two WS calls)</p> <p>Well, follow the tutorial :-). Then duplicate the web service call sampler, call the first one WS: Authenticate with Saba and the other one WS: PF - Update employees.</p> <p>2. Add the User Defined Variables config element to the test plan and define there a variable for transferring the response data</p> <p>We will need a variable to hold the data that we want to transfer from the 1st response to a subsequent request. Therefore open the test plan, right-click on Thread Group &gt; Add &gt; Config Element &gt; User Defined Variables. Add there a variable named sabaCertificate. You can leave its Value empty.</p> <p>3. Add an XPath Extractor Post Processor to the first WS call to extract the value of interest into the user-defined variable</p> <p>Now we will extract the "certificate" data from the first response. The response may look like this (I used Eclipse' TCP Monitor to capture the SOAP communication):</p> <pre class="language-markup"><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt; &lt;soapenv:Body&gt; &lt;saba:certificate xmlns:saba="http://www.saba.com/xml/infoservices"&gt;31323930326436636637635&lt;/saba:certificate&gt; &lt;/soapenv:Body&gt; &lt;/soapenv:Envelope&gt;</code></pre> <p>To extract the value of the element &lt;saba:certificate&gt;:</p> <p>Right-click on the first WS call (WS: Authenticate with Saba) and Add &gt; Post Processors &gt; XPath Extractor</p> <p>For the Reference Name, type sabaCertificate (the user variable we've created earlier)</p> <p>For the XPath query, type //*[local-name()='certificate']/text()</p> <p>Problem with namespaces: Beware that JMeter 2.3.4 supports only namespaces declared on the root element and thus the XPath query //saba:certificate wouldn't work. The documentation for XPath Extractor's attribute "Use Namespace?" provides a workaround based on using the functions local-name() and namespace-uri() to match the local tag name and the URI associated with its namespace, which I've partly used.</p> <p>You can test your XPath for example in the Allans Online XPath Tester</p> <p>&nbsp;</p> <p>4. Add a BeanShell PreProcessor to the second call, which will replace a placeholder in the WS call's XML data with the value of that variable</p> <p>Now we need to get the "certificate" into the subsequent web service request. I have put the placeholder "#sabaCertificate#" into the SOPA request, at the place where the actual authentication token shall be. Now we will arrange for its replacement with the actual value:</p> <p>Right-click on the second WS call (WS: PF - Update employees) and Add &gt; Pre Processors &gt; BeanShell PreProcessor (BeanShell is a scripting language with Java syntax and is included in JMeter)</p> <p>Type in the following script (notice that sampler is a variable provided by JMeter and refers to the parent WS call; check JavaDoc for details on the WebServiceSampler):</p> <p>&nbsp;</p> <p>Case 1: SOAP request specified directly in the attribute Soap/XML-RPC Data</p> <pre class="language-java"><code>import org.apache.jmeter.protocol.http.sampler.WebServiceSampler; WebServiceSampler wsSampler = (WebServiceSampler) sampler; String requestWithCertif = wsSampler.getXmlData().replaceFirst("#sabaCertificate#", vars.get("sabaCertificate")); wsSampler.setXmlData(requestWithCertif);</code></pre> <p>Case 2: The SOAP request is read from a file (attribute File with SOAP XML Data)</p> <p>If the request data is read from a file then it's a bit more complex because we need to load its content.</p> <pre class="language-java"><code>import org.apache.jmeter.protocol.http.sampler.WebServiceSampler; import java.io.*; WebServiceSampler wsSampler = (WebServiceSampler) sampler; BufferedReader xmlReader = new BufferedReader( new InputStreamReader( new FileInputStream(wsSampler.getXmlFile()) , java.nio.charset.Charset.forName("UTF-8") )); StringBuffer xmlData = new StringBuffer(); String line; while( (line = xmlReader.readLine()) != null) { xmlData.append(line).append('\n'); } String requestWithCertif = xmlData.toString().replaceFirst("#sabaCertificate#", vars.get("sabaCertificate")); wsSampler.setXmlData(requestWithCertif); wsSampler.setXmlFile("") ; // a file would override the data // print("XML set: " + requestWithCertif); // print to the console JMeter was started from</code></pre> <p>Well, that's it!</p> <p>Going advanced: Reading requests from several files</p> <p>The approach described above makes it possible to send a request based on a single file. But what if we want to send a different data with each repetition of the test, e.g. to negate effects of caching? Well, there is a couple of ways to achieve that. I've chosen the most flexible one, though absolutely not the easiest one to implement.</p> <p>&nbsp;</p> <p>The trick is:</p> <p>Create a BeanShell Sampler. The sampler will list all files in a particular directory and store their paths into numbered variables (G_updateEmployeesWsRequestFile_1 etc., must start with 1), which will be then used by a ForEach Controller.</p> <p>Put all the test elements from the basic test plan under a ForEach Controller, which follows the BeanShell Sampler. Configure it to use the variables generated by the BeanShell Sampler and store the current file name in the variable G_updateEmployeesWsRequestFile.</p> <p>In the web service request element, replace the content of the Filename field with a reference to that variable: ${G_updateEmployeesWsRequestFile}</p> <p>The BeanShell Sampler "Generate WS request file names"</p> <pre class="language-java"><code>import java.io.*; print("Generating files..."); log.info("BeanShell Sampler: Generating request file names..."); File requestsDir = new File("/tmp/wsRequests"); String[] requestFiles = requestsDir.list(); for(int i=0; i&lt;requestFiles.length; ++i) { String varName = "G_updateEmployeesWsRequestFile_" + (i+1); vars.put( varName , requestsDir.getAbsolutePath() + File.separatorChar + requestFiles[i] ); // print("var created: " + varName + "=" + vars.get(varName)); } log.info("BeanShell Sampler: FINISHED generating request file names from dir " + requestsDir + "; files are: " + java.util.Arrays.asList(requestFiles)); return "soap input files generated";</code></pre> <p>The ForEach Controller "ForEach request file"</p> <p>The controller's configuration is simple:</p> <p>Input variable prefix: G_updateEmployeesWsRequestFile</p> <p>Output variable name: G_updateEmployeesWsRequestFile</p> <p>Add "_" before before number: [x] (checked)</p> <p>Summary</p> <p>We've parametrized the test by a set of files with SOAP requests that are read from a folder and supplied sequentially to the test thanks to the ForEach Controller.</p> <p>&nbsp;</p> <p>&nbsp;</p>

read more...