Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract xml node contents without node itself into variable in jmeter

Tags:

xpath

jmeter

In my JMeter scenario I'm testing SOAP webservices. I send the initial request to webservice S1 and then use response from it to build request to S2. The response from S1 comes in the form:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
    <s:response xmlns:s="http://custom.namespace.com/namespace">
    ...
        <s:responseDetails>
            <s:detail1>some info</s:detail>
            <s:detail2>another info<s:detail2>
            ...
        </s:responseDetails>
    </s:response>
</soap:Body>
</soap:Envelope>

I need to use everything inside <s:responseDetails> to build request to S2 but wrapped inside different xml element:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:s="http://custom.namespace.com/namespace">
<soap:Body>
    <s:request>
        <s:details>
            <s:detail1>some info</s:detail>
            <s:detail2>another info<s:detail2>
            ...
        </s:details>
    </s:request>
</soap:Body>
</soap:Envelope>

Note element <s:details> in request instead of <s:responseDetails>.

I started with XPath extractor hoping to use XPath expression to put everything under <s:responseDetails> tag into JMeter variable:

  • //*[local-name()='responseDetails']/node() - gives me a set of JMeter variables with different children of <s:responseDetails>, while I want them to be in one variable
  • //*[local-name()='responseDetails'] - gives me the entire node <s:responseDetails> which I can't use because of the presence of root tag <s:responseDetails>, which I need to replace with <s:details>

What is the proper expression to get everything inside <s:responseDetails> into variable, so that I can later use

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:s="http://custom.namespace.com/namespace">
<soap:Body>
    <s:request>
        <s:details>
            ${details}
        </s:details>
    </s:request>
</soap:Body>
</soap:Envelope>

Or maybe there is another way to solve my problem?

like image 329
pavel_kazlou Avatar asked Sep 03 '12 10:09

pavel_kazlou


1 Answers

Do the following:

1) Add XPath Extractor as you did with:

  • Reference name: responseFragment
  • XPath query : //s:response/s:responseDetails
  • Use namespaces=true

2) Add a Beanshell Sampler with:

String fragment = vars.get("responseFragment");
// Improve this replacement by regexp to avoid side effects
String newFragment = fragment.replace("responseDetails","details");
 vars.put("modifiedFragment", newFragment);

3) Then use : ${modifiedFragment}

Note that Beanshell sampler will slightly degrade JMeter performances but it should be reasonable.

Note that you can replace Beanshell sampler with JSR 223 with groovy underlying language and script in external file.

Using JMeter > 2.7 version performances will be better than beanshell solution because new version of JMeter introduces a new caching mechanism that will "compile" the Groovy script and make it almost as efficient as Java class.

As of 3 september 2012, this mechanism is available in JMeter Nightly build.

JMeter Test Plan example:

    <?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.3" jmeter="2.8-SNAPSHOT.20120903">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
      <stringProp name="TestPlan.comments"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
          <boolProp name="LoopController.continue_forever">false</boolProp>
          <stringProp name="LoopController.loops">1</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">1</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <longProp name="ThreadGroup.start_time">1346673693000</longProp>
        <longProp name="ThreadGroup.end_time">1346673693000</longProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
      </ThreadGroup>
      <hashTree>
        <JavaSampler guiclass="JavaTestSamplerGui" testclass="JavaSampler" testname="Java Request" enabled="true">
          <elementProp name="arguments" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" enabled="true">
            <collectionProp name="Arguments.arguments">
              <elementProp name="Sleep_Time" elementType="Argument">
                <stringProp name="Argument.name">Sleep_Time</stringProp>
                <stringProp name="Argument.value">100</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="Sleep_Mask" elementType="Argument">
                <stringProp name="Argument.name">Sleep_Mask</stringProp>
                <stringProp name="Argument.value">0xFF</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="Label" elementType="Argument">
                <stringProp name="Argument.name">Label</stringProp>
                <stringProp name="Argument.value">OK</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="ResponseCode" elementType="Argument">
                <stringProp name="Argument.name">ResponseCode</stringProp>
                <stringProp name="Argument.value">200</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="ResponseMessage" elementType="Argument">
                <stringProp name="Argument.name">ResponseMessage</stringProp>
                <stringProp name="Argument.value">200</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="Status" elementType="Argument">
                <stringProp name="Argument.name">Status</stringProp>
                <stringProp name="Argument.value">OK</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="SamplerData" elementType="Argument">
                <stringProp name="Argument.name">SamplerData</stringProp>
                <stringProp name="Argument.value"></stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
              <elementProp name="ResultData" elementType="Argument">
                <stringProp name="Argument.name">ResultData</stringProp>
                <stringProp name="Argument.value">&lt;soap:Envelope xmlns:soap=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot;&gt; &lt;soap:Body&gt;     &lt;s:response xmlns:s=&quot;http://custom.namespace.com/namespace&quot;&gt;         &lt;s:responseDetails&gt;             &lt;s:detail1&gt;some info&lt;/s:detail1&gt;             &lt;s:detail2&gt;another info&lt;/s:detail2&gt;         &lt;/s:responseDetails&gt;     &lt;/s:response&gt; &lt;/soap:Body&gt; &lt;/soap:Envelope&gt;</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
          <stringProp name="classname">org.apache.jmeter.protocol.java.test.JavaTest</stringProp>
        </JavaSampler>
        <hashTree>
          <XPathExtractor guiclass="XPathExtractorGui" testclass="XPathExtractor" testname="XPath Extractor" enabled="true">
            <stringProp name="XPathExtractor.default"></stringProp>
            <stringProp name="XPathExtractor.refname">TOTO</stringProp>
            <stringProp name="XPathExtractor.xpathQuery">//s:response/s:responseDetails</stringProp>
            <boolProp name="XPathExtractor.validate">false</boolProp>
            <boolProp name="XPathExtractor.tolerant">false</boolProp>
            <boolProp name="XPathExtractor.namespace">true</boolProp>
            <boolProp name="XPathExtractor.whitespace">true</boolProp>
          </XPathExtractor>
          <hashTree/>
        </hashTree>
        <DebugSampler guiclass="TestBeanGUI" testclass="DebugSampler" testname="Debug Sampler" enabled="true">
          <boolProp name="displayJMeterProperties">false</boolProp>
          <boolProp name="displayJMeterVariables">true</boolProp>
          <boolProp name="displaySystemProperties">false</boolProp>
        </DebugSampler>
        <hashTree/>
        <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
          <boolProp name="ResultCollector.error_logging">false</boolProp>
          <objProp>
            <name>saveConfig</name>
            <value class="SampleSaveConfiguration">
              <time>true</time>
              <latency>true</latency>
              <timestamp>true</timestamp>
              <success>true</success>
              <label>true</label>
              <code>true</code>
              <message>false</message>
              <threadName>true</threadName>
              <dataType>false</dataType>
              <encoding>false</encoding>
              <assertions>true</assertions>
              <subresults>false</subresults>
              <responseData>false</responseData>
              <samplerData>false</samplerData>
              <xml>false</xml>
              <fieldNames>false</fieldNames>
              <responseHeaders>false</responseHeaders>
              <requestHeaders>false</requestHeaders>
              <responseDataOnError>false</responseDataOnError>
              <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
              <assertionsResultsToSave>0</assertionsResultsToSave>
              <bytes>true</bytes>
              <hostname>true</hostname>
              <threadCounts>true</threadCounts>
              <sampleCount>true</sampleCount>
            </value>
          </objProp>
          <stringProp name="filename"></stringProp>
        </ResultCollector>
        <hashTree/>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

An other way:

1) Add XPath Extractor as you did with:

  • Reference name: responseFragment
  • XPath query : //s:response/s:responseDetails/*
  • Use namespaces=true

2) Add a Beanshell Sampler with:

int number = Integer.parseInt(vars.get("fullAnswer_matchNr"));
StringBuilder builder = new StringBuilder();
for(int i=0;i<number;i++) {
     builder.append(vars.get("fullAnswer_"+(i+1)));
}
vars.put("body", builder.toString());

3) use body

This one is a little cleaner as there is no replacement

like image 58
UBIK LOAD PACK Avatar answered Oct 11 '22 09:10

UBIK LOAD PACK