SoapUI: dynamischer Mockservice (Teil 1)

Mit Hilfe von SoapUI kann man ganz einfach einen Mockservice aufsetzen, welcher statische Responses liefern kann. Es ist aber auch mit wenigen Handgriffen möglich, dynamisch generierte Responses zu liefern - welche abhängig von jeweiligen Request die Response anpasst.

Grundlage ist ein existierendes Projekt in SoapUI auf Basis einer WSDL. In meinem Beispiel ein einfacher Service mit nur einer Methode. Für diese Methode gibt es auch schon einen Beispiel-Request, welchen SoapUI für mich bereits angelegt hat.

image0

Im Kontext-Menü des Requests wählt man "Add to Mock Service". Damit wird für diesen Request ein neuer Mockservice erstellt.

image1

Die Frage, ob eine Default Mock Response erzeugt werden soll, beantwortet man mit "JA".

image2

Danach vergibt noch einen Namen für den Mockservice (oder lässt den Standard). Es wurde ein neuer Mockservice erstellt, welcher eine Default-Response enthält. Diese Response sieht wie folgt aus:

image3

In der generierte Response können nun Platzhalter definiert werden, welche man mittels Groovy mit Werten belegen kann.

image4

Als nächstes muss man einstellen, wie sich der Mockservice beim Eintreffen eines Request verhalten soll. Dazu öffnet man den Mock Operation Editor. Da ich hier den Einsatz von Groovy plane, muss als Dispatch-Methode "Script" ausgewählt werden. Als Default-Response die (einzige) Response auswählen.

Im unteren Bereich des Fensters kann das Groovyskript eingefügt werden. Für den Anfang ersetze ich die Platzhalter mit statischen Werten.

image5

Das war es schon. Jetzt müssen ggf. die Einstellungen für den Mockservice überprüft werden. Die Einstellungen werden im Mockservice Editor vorgenommen.

image6

Hier gibt Mockservice Options, in denen man den Port und den Contextpfad des Mockservice anpassen kann.

image7

Damit ist der Mockservice fertig.

image8

Hier ist die vollständige WSDL für den Test:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:tns="http://test.ronnyfriedland.de" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="test"
 targetNamespace="http://test.ronnyfriedland.de">
 <wsdl:types>
  <xsd:schema targetNamespace="http://test.ronnyfriedland.de">
   <xsd:element name="getUserData">
    <xsd:complexType>
     <xsd:sequence>

      <xsd:element name="id" type="xsd:int" minOccurs="1"
       maxOccurs="1"></xsd:element>
     </xsd:sequence>
    </xsd:complexType>
   </xsd:element>
   <xsd:element name="getUserDataResponse">
    <xsd:complexType>
     <xsd:sequence>
      <xsd:element name="data" type="tns:UserData" />
     </xsd:sequence>
    </xsd:complexType>
   </xsd:element>

   <xsd:complexType name="UserData">
    <xsd:sequence>
     <xsd:element name="id" type="xsd:int" minOccurs="1"
      maxOccurs="1"></xsd:element>
     <xsd:element name="firstname" type="xsd:string"
      minOccurs="1" maxOccurs="1"></xsd:element>
     <xsd:element name="lastname" type="xsd:string"
      minOccurs="1" maxOccurs="1"></xsd:element>
     <xsd:element name="image" type="xsd:base64Binary"
      minOccurs="1" maxOccurs="1"></xsd:element>
    </xsd:sequence>
   </xsd:complexType>
   <xsd:element name="getUserDataFault">
    <xsd:complexType>
     <xsd:sequence>

      <xsd:element name="UserNotFoundFault" type="xsd:string">
      </xsd:element>
     </xsd:sequence>
    </xsd:complexType>
   </xsd:element>
  </xsd:schema>
 </wsdl:types>
 <wsdl:message name="getUserDataRequest">
  <wsdl:part element="tns:getUserData" name="parameters" />
 </wsdl:message>
 <wsdl:message name="getUserDataResponse">
  <wsdl:part element="tns:getUserDataResponse" name="parameters" />
 </wsdl:message>
 <wsdl:message name="getUserDataFault">
  <wsdl:part name="parameters" element="tns:getUserDataFault"></wsdl:part>
 </wsdl:message>
 <wsdl:portType name="test">
  <wsdl:operation name="getUserData">
   <wsdl:input message="tns:getUserDataRequest" />
   <wsdl:output message="tns:getUserDataResponse" />
   <wsdl:fault name="fault" message="tns:getUserDataFault"></wsdl:fault>
  </wsdl:operation>
 </wsdl:portType>
 <wsdl:binding name="testSOAP" type="tns:test">

  <soap:binding style="document"
   transport="http://schemas.xmlsoap.org/soap/http" />
  <wsdl:operation name="getUserData">

   <soap:operation soapAction="http://test.ronnyfriedland.de/getUserData" />
   <wsdl:input>

    <soap:body use="literal" />
   </wsdl:input>
   <wsdl:output>

    <soap:body use="literal" />
   </wsdl:output>
   <wsdl:fault name="fault">
    <soap:fault use="literal" name="fault" />
   </wsdl:fault>
  </wsdl:operation>
 </wsdl:binding>
 <wsdl:service name="test">
  <wsdl:port binding="tns:testSOAP" name="test-port">
   <soap:address location="http://test.ronnyfriedland.de" />
  </wsdl:port>
 </wsdl:service>
</wsdl:definitions>

Die Konfiguration für den Mockservice kann man als XML abspeichern, und somit ganz einfach in SoapUI importieren.

<?xml version="1.0" encoding="UTF-8"?>
<con:soapui-project name="GroovyTest" resourceRoot="" soapui-version="3.5.1" xmlns:con="http://eviware.com/soapui/config"><con:settings/><con:interface xsi:type="con:WsdlInterface" wsaVersion="NONE" name="testSOAP" type="wsdl" bindingName="{http://test.ronnyfriedland.de}testSOAP" soapVersion="1_1" anonymous="optional" definition="file:/test.wsdl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:settings/><con:definitionCache type="TEXT" rootPart="file:/test.wsdl"><con:part><con:url>file:/test.wsdl</con:url><con:content><![CDATA[<wsdl:definitions name="test" targetNamespace="http://test.ronnyfriedland.de" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://test.ronnyfriedland.de" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <wsdl:types>
    <xsd:schema targetNamespace="http://test.ronnyfriedland.de">
      <xsd:element name="getUserData">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="id" type="xsd:int" minOccurs="1" maxOccurs="1"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="getUserDataResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="data" type="tns:UserData"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:complexType name="UserData">
        <xsd:sequence>
          <xsd:element name="id" type="xsd:int" minOccurs="1" maxOccurs="1"/>
          <xsd:element name="firstname" type="xsd:string" minOccurs="1" maxOccurs="1"/>
          <xsd:element name="lastname" type="xsd:string" minOccurs="1" maxOccurs="1"/>
          <xsd:element name="image" type="xsd:base64Binary" minOccurs="1" maxOccurs="1"/>
        </xsd:sequence>
      </xsd:complexType>
      <xsd:element name="getUserDataFault">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="UserNotFoundFault" type="xsd:string"></xsd:element>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="getUserDataRequest">
    <wsdl:part element="tns:getUserData" name="parameters"/>
  </wsdl:message>
  <wsdl:message name="getUserDataResponse">
    <wsdl:part element="tns:getUserDataResponse" name="parameters"/>
  </wsdl:message>
  <wsdl:message name="getUserDataFault">
    <wsdl:part name="parameters" element="tns:getUserDataFault"/>
  </wsdl:message>
  <wsdl:portType name="test">
    <wsdl:operation name="getUserData">
      <wsdl:input message="tns:getUserDataRequest"/>
      <wsdl:output message="tns:getUserDataResponse"/>
      <wsdl:fault name="fault" message="tns:getUserDataFault"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="testSOAP" type="tns:test">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getUserData">
      <soap:operation soapAction="http://test.ronnyfriedland.de/getUserData"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
      <wsdl:fault name="fault">
        <soap:fault use="literal" name="fault"/>
      </wsdl:fault>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="test">
    <wsdl:port binding="tns:testSOAP" name="test-port">
      <soap:address location="http://test.ronnyfriedland.de"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>]]></con:content><con:type>http://schemas.xmlsoap.org/wsdl/</con:type></con:part></con:definitionCache><con:endpoints><con:endpoint>http://localhost:8080/test</con:endpoint><con:endpoint>http://test.ronnyfriedland.de</con:endpoint></con:endpoints><con:operation isOneWay="false" action="http://test.ronnyfriedland.de/getUserData" name="getUserData" bindingOperationName="getUserData" type="Request-Response" inputName="" receivesAttachments="false" sendsAttachments="false" anonymous="optional"><con:settings/><con:call name="Request 1"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers">&lt;xml-fragment/></con:setting></con:settings><con:encoding>UTF-8</con:encoding><con:endpoint>http://localhost:8080/test</con:endpoint><con:request><![CDATA[<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:test="http://test.ronnyfriedland.de">
   <soapenv:Header/>
   <soapenv:Body>
      <test:getUserData>

<!--type: int-->
         <id>1</id>
      </test:getUserData>
   </soapenv:Body>
</soapenv:Envelope>]]></con:request><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:wsaConfig mustUnderstand="NONE" version="200508" action="http://test.ronnyfriedland.de/test/getUserDataRequest"/><con:wsrmConfig version="1.2"/></con:call></con:operation></con:interface><con:mockService port="8080" path="/test" host="localhost" name="GroovyMockService" bindToHostOnly="false" docroot=""><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.mock.WsdlMockService@require-soap-action">false</con:setting></con:settings><con:mockOperation name="getUserData" interface="testSOAP" operation="getUserData"><con:settings/><con:dispatchPath>context.id = "1"
context.firstname = "Vorname des Nutzers"
context.lastname = "Nachname des Nutzers"
context.image = javax.xml.bind.DatatypeConverter.printBase64Binary(new byte[0])</con:dispatchPath><con:dispatchStyle>SCRIPT</con:dispatchStyle><con:defaultResponse>Response 1</con:defaultResponse><con:response name="Response 1" encoding="UTF-8"><con:settings/><con:responseContent><![CDATA[<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:test="http://test.ronnyfriedland.de">
   <soapenv:Header/>
   <soapenv:Body>
      <test:getUserDataResponse>
         <data>
            <id>${id}</id>
            <firstname>${firstname}</firstname>
            <lastname>${lastname}</lastname>
            <image>${image}</image>
         </data>
      </test:getUserDataResponse>
   </soapenv:Body>
</soapenv:Envelope>]]></con:responseContent><con:wsaConfig mustUnderstand="NONE" version="200508" action="http://test.ronnyfriedland.de/test/getUserDataResponse"/></con:response><con:dispatchConfig/></con:mockOperation><con:properties/></con:mockService><con:properties/><con:wssContainer/></con:soapui-project>