The following example describes one limitation of soapUI and a possible workaround. It concerns the way mock services are used in mock response test steps.
We consider a web service (TripPriceService) that offers an operation to compute the price of a trip composed of many flights. This web service uses another web service (FlightPriceService), that returns the price of a single flight between 2 locations.
Formula : price = adults * sum(getFlightPrice(from[i], to[i]))
Here are the WSDLs of the 2 services:
TripPriceService.wsdl
<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions name="TripPriceServiceFacadeService"
targetNamespace="http://trip.price.service" xmlns:ns1="http://cxf.apache.org/bindings/xformat"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://trip.price.service"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xs:schema attributeFormDefault="unqualified"
elementFormDefault="unqualified" targetNamespace="http://trip.price.service"
xmlns:tns="http://trip.price.service" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="getCompleteFlightPrice" type="tns:getCompleteFlightPrice" />
<xs:element name="getCompleteFlightPriceResponse" type="tns:getCompleteFlightPriceResponse" />
<xs:complexType name="getCompleteFlightPrice">
<xs:sequence>
<xs:element minOccurs="0" name="trip" type="tns:completeTrip" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="completeTrip">
<xs:sequence>
<xs:element name="adults" type="xs:int" />
<xs:element maxOccurs="unbounded" minOccurs="0"
name="trips" nillable="true" type="tns:tripPart" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="tripPart">
<xs:sequence>
<xs:element minOccurs="0" name="from" type="xs:string" />
<xs:element minOccurs="0" name="to" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="getCompleteFlightPriceResponse">
<xs:sequence>
<xs:element name="return" type="xs:float" />
</xs:sequence>
</xs:complexType>
<xs:element name="TripPriceServiceException" type="tns:TripPriceServiceException" />
<xs:complexType name="TripPriceServiceException">
<xs:sequence />
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="TripPriceServiceException">
<wsdl:part element="tns:TripPriceServiceException" name="TripPriceServiceException">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getCompleteFlightPriceResponse">
<wsdl:part element="tns:getCompleteFlightPriceResponse"
name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getCompleteFlightPrice">
<wsdl:part element="tns:getCompleteFlightPrice" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="ITripPriceServiceFacade">
<wsdl:operation name="getCompleteFlightPrice">
<wsdl:input message="tns:getCompleteFlightPrice" name="getCompleteFlightPrice">
</wsdl:input>
<wsdl:output message="tns:getCompleteFlightPriceResponse"
name="getCompleteFlightPriceResponse">
</wsdl:output>
<wsdl:fault message="tns:TripPriceServiceException"
name="TripPriceServiceException">
</wsdl:fault>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="TripPriceServiceFacadeServiceSoapBinding"
type="tns:ITripPriceServiceFacade">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="getCompleteFlightPrice">
<soap:operation soapAction="" style="document" />
<wsdl:input name="getCompleteFlightPrice">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="getCompleteFlightPriceResponse">
<soap:body use="literal" />
</wsdl:output>
<wsdl:fault name="TripPriceServiceException">
<soap:fault name="TripPriceServiceException" use="literal" />
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="TripPriceServiceFacadeService">
<wsdl:port binding="tns:TripPriceServiceFacadeServiceSoapBinding"
name="TripPriceServiceFacadePort">
<soap:address
location="http://localhost:8080/complete-trip-price-0.0.1-SNAPSHOT/webservices/TripPriceService" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
FlightPriceService.wsdl
<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions name="FlightPriceServiceFacadeService"
targetNamespace="http://external.services/flight" xmlns:ns1="http://cxf.apache.org/bindings/xformat"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://external.services/flight"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xs:schema attributeFormDefault="unqualified"
elementFormDefault="unqualified" targetNamespace="http://external.services/flight"
xmlns:tns="http://external.services/flight" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="getFlightPrice" type="tns:getFlightPrice" />
<xs:element name="getFlightPriceResponse" type="tns:getFlightPriceResponse" />
<xs:complexType name="getFlightPrice">
<xs:sequence>
<xs:element minOccurs="0" name="from" type="xs:string" />
<xs:element minOccurs="0" name="to" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="getFlightPriceResponse">
<xs:sequence>
<xs:element name="return" type="xs:float" />
</xs:sequence>
</xs:complexType>
<xs:element name="LocationNotFoundException" type="tns:LocationNotFoundException" />
<xs:complexType name="LocationNotFoundException">
<xs:sequence />
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="LocationNotFoundException">
<wsdl:part element="tns:LocationNotFoundException" name="LocationNotFoundException">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getFlightPrice">
<wsdl:part element="tns:getFlightPrice" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getFlightPriceResponse">
<wsdl:part element="tns:getFlightPriceResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="IFlightPriceServiceFacade">
<wsdl:operation name="getFlightPrice">
<wsdl:input message="tns:getFlightPrice" name="getFlightPrice">
</wsdl:input>
<wsdl:output message="tns:getFlightPriceResponse"
name="getFlightPriceResponse">
</wsdl:output>
<wsdl:fault message="tns:LocationNotFoundException"
name="LocationNotFoundException">
</wsdl:fault>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="FlightPriceServiceFacadeServiceSoapBinding"
type="tns:IFlightPriceServiceFacade">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="getFlightPrice">
<soap:operation soapAction="" style="document" />
<wsdl:input name="getFlightPrice">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="getFlightPriceResponse">
<soap:body use="literal" />
</wsdl:output>
<wsdl:fault name="LocationNotFoundException">
<soap:fault name="LocationNotFoundException" use="literal" />
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="FlightPriceServiceFacadeService">
<wsdl:port binding="tns:FlightPriceServiceFacadeServiceSoapBinding"
name="FlightPriceServiceFacadePort">
<soap:address
location="http://localhost:8088/external-services-0.0.1-SNAPSHOT/webservices/FlightPriceService" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
We imagine one adult wants to fly from Berlin to Paris and then from Paris to London. The flight from Berlin to Paris costs 15.0 Euro and the flight from Paris to London 100.0 Euro. So we expect the web service to return 115.0.
Let us create the test case in soapUI.
1. Create a new project with all the interfaces
- Select "File" -> "New soapUI Project"
- Set the project name: complete-trip-price
- Set the initial WSDL: http://localhost:8080/complete-trip-price-0.0.1-SNAPSHOT/webservices/TripPriceService?wsdl
- Click "OK"
- Right click the project and select "Add WSDL"
- Set the WSDL location: /home/ubuntu/workspace/test-webservice-soapui/complete-trip-price/complete-trip-price-service/src/main/resources/wsdl/FlightPriceService.wsdl
2. Generate a test suite and a test case for the getCompleteFlightPrice operation
- Right click the "TripPriceServiceFacadeServiceSoapBinding" and select "Generate TestSuite"
- Click "OK"
- Enter a name for the test suite: TripPriceServiceFacadeServiceSoapBinding TestSuite
- Click "OK"
- Set a timeout for the test case
3. Configure the test request
- Edit the test request content
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:trip="http://trip.price.service">
<soapenv:Header/>
<soapenv:Body>
<trip:getCompleteFlightPrice>
<trip>
<adults>1</>
<trips>
<from>Berlin</from>
<to>Paris</to>
</trips>
<trips>
<from>Paris</from>
<to>London</to>
</trips>
</trip>
</trip:getCompleteFlightPrice>
</soapenv:Body>
</soapenv:Envelope>
- Add an "XPath Match" assertion to check the price
XPath Expression
declare namespace soap='http://schemas.xmlsoap.org/soap/envelope/'; declare namespace ns1='http://trip.price.service'; //ns1:getCompleteFlightPriceResponse/return
Expected Result
115.0
4. Configure the mock responses
- Append a mock response test step
- Specify a name for it, for eample "getFlightPrice Berlin-Paris Mock Response"
- Select the getFlightPrice operation of the "FlightPriceServiceFacadeServiceSoapBinding" interface
- Set the port to listen on 8088
- Set the path to listen on /external-services-0.0.1-SNAPSHOT/webservices/FlightPriceService
- Click "OK"
- Edit the content of the response to return 15.0
- Set the start step of the mock response : getCompleteFlightPrice
- Append a mock response test step
- Specify a name for it, for eample "getFlightPrice Paris-London Mock Response"
- Select the getFlightPrice operation of the "FlightPriceServiceFacadeServiceSoapBinding" interface
- Set the port to listen on 8088
- Set the path to listen on /external-services-0.0.1-SNAPSHOT/webservices/FlightPriceService
- Click "OK"
- Edit the content of the response to return 100.0
- Set the start step of the mock response : getCompleteFlightPrice
5. Run the test case
- Click the "Runs this testcase" button
The test case is failed, because of the XPath Match assertion. The web service returns 30.0 instead of 115.0.
In fact our first mock response test step has answered twice.
Delete the 2 mock response test steps from the test case... we will try something else.
6. Workaround
A possible workaround is to create a mock service with a sequence of responses and to start it via groovy script when launching the test case.
6.1 Create a mock service
- Right click the "FlightPriceServiceFacadeServiceSoapBinding" interface and select "Generate MockService"
- Set the port to listen on 8088
- Set the path to listen on /external-services-0.0.1-SNAPSHOT/webservices/FlightPriceService
- Click "OK"
- Specify the name of the mock service, for example "FlightPriceService MockService" and click "OK"
- Edit the generated mock response to return 15.0
- Rename the mock response to "MockResponse Berlin-Paris"
- Right click the getFlightPrice operation of the mock service and select "New MockResponse"
- Enter a name for the mock response, for example "MockResponse Paris-London" and click "OK"
- Edit the generated mock response to return 100.0
- Check the dispatch style of the operation is sequence
6.2 Start the mock service in a setup script
- Double click the test case and click the "Setup Script" tab
- Enter the following script
def project = testCase.getTestSuite().getProject();
def mockService = project.getMockServiceByName("FlightPriceService MockService");
mockService.start();
6.3 Stop the mock service in a teardown script
- Click the "TearDown Script" tab
- Enter the following script
def project = testCase.getTestSuite().getProject();
def mockService = project.getMockServiceByName("FlightPriceService MockService");
def mockRunner = mockService.getMockRunner();
mockRunner.stop();
Now run you test case again, it should be successful.
Technorati Tags: soapUI mock response groovy script mock service

Thank you nadege for the article.
I am novice user of soapUI.
The description of mock services in Getting Started Guide on soapUI website (www.soapui.org) is confusing, specifically step 6 onwards in '4) Customizing a MockResponse'.
Please enlighten the meaning of these steps.