SoapUI is a useful tool for testing web services. Not only makes it possible to test deployed web services, but it also offers the possibility to mock any "external" service needed by the service you want to test. As a result, you can test your web service independently from any other external service.
The following basic example describes how to test a service that computes the price of a trip (TripPriceService). It invokes one service that returns prices for hotel rooms (HotelPriceService) and one service that returns prices for flights (FlightPriceService).
Formula : price = duration * rooms * getRoomPrice() + adults * getFlightPrice(from, to)
Here are the WSDLs of the three 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="getTripPrice" type="tns:getTripPrice" />
<xs:element name="getTripPriceResponse" type="tns:getTripPriceResponse" />
<xs:complexType name="getTripPrice">
<xs:sequence>
<xs:element minOccurs="0" name="trip" type="tns:trip" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="trip">
<xs:sequence>
<xs:element name="adults" type="xs:int" />
<xs:element name="duration" type="xs:int" />
<xs:element minOccurs="0" name="from" type="xs:string" />
<xs:element name="rooms" type="xs:int" />
<xs:element minOccurs="0" name="to" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="getTripPriceResponse">
<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="getTripPrice">
<wsdl:part element="tns:getTripPrice" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getTripPriceResponse">
<wsdl:part element="tns:getTripPriceResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="ITripPriceServiceFacade">
<wsdl:operation name="getTripPrice">
<wsdl:input message="tns:getTripPrice" name="getTripPrice">
</wsdl:input>
<wsdl:output message="tns:getTripPriceResponse" name="getTripPriceResponse">
</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="getTripPrice">
<soap:operation soapAction="" style="document" />
<wsdl:input name="getTripPrice">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="getTripPriceResponse">
<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/trip-price-0.0.1-SNAPSHOT/webservices/TripPriceService" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
HotelPriceService.wsdl
<?xml version='1.0' encoding='UTF-8'?><wsdl:definitions name="HotelPriceServiceFacadeService"
targetNamespace="http://external.services/hotel" xmlns:ns1="http://cxf.apache.org/bindings/xformat"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://external.services/hotel"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xs:schema elementFormDefault="unqualified" targetNamespace="http://external.services/hotel"
version="1.0" xmlns:tns="http://external.services/hotel" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="getRoomPrice" type="tns:getRoomPrice" />
<xs:element name="getRoomPriceResponse" type="tns:getRoomPriceResponse" />
<xs:complexType name="getRoomPrice">
<xs:sequence />
</xs:complexType>
<xs:complexType name="getRoomPriceResponse">
<xs:sequence>
<xs:element name="return" type="xs:float" />
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="getRoomPrice">
<wsdl:part element="tns:getRoomPrice" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getRoomPriceResponse">
<wsdl:part element="tns:getRoomPriceResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="IHotelPriceServiceFacade">
<wsdl:operation name="getRoomPrice">
<wsdl:input message="tns:getRoomPrice" name="getRoomPrice">
</wsdl:input>
<wsdl:output message="tns:getRoomPriceResponse" name="getRoomPriceResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="HotelPriceServiceFacadeServiceSoapBinding" type="tns:IHotelPriceServiceFacade">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="getRoomPrice">
<soap:operation soapAction="" style="document" />
<wsdl:input name="getRoomPrice">
<soap:body use="literal" />
</wsdl:input>
<wsdl:output name="getRoomPriceResponse">
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HotelPriceServiceFacadeService">
<wsdl:port binding="tns:HotelPriceServiceFacadeServiceSoapBinding" name="HotelPriceServiceFacadePort">
<soap:address location="http://localhost:8088/external-services-0.0.1-SNAPSHOT/webservices/HotelPriceService" />
</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>
For the next steps we assume the web service we want to test is deployed and the external services are not.
1. How to create a soapUI project
- In the menu select "File" -> "New soapUI Project"
- Set the project name, for example "trip-price-service"
- Set the initial WSDL, for example "http://localhost:8080/trip-price-0.0.1-SNAPSHOT/webservices/TripPriceService?wsdl"
- Click "OK"
As a result, you get the "trip-price-service" project containing the "TripPriceServiceFacadeServiceSoapBinding" interface.
2. How to generate a test suite
- Right click on the "TripPriceServiceFacadeServiceSoapBinding" interface and select "Generate TestSuite"
- Click "OK"
- Enter a name for your test suite, for example "TripPriceServiceFacadeServiceSoapBinding TestSuite"
- Click "OK"
As a result you get the "TripPriceServiceFacadeServiceSoapBinding TestSuite" test suite, that includes the "getTripPrice TestCase" test case. The test case already contains the generated "getTripPrice" test request.
3. How to configure the test case
Setting a timeout for the test case is a good habit, so your test does not run forever, in case something goes wrong.
- Double click the test case
- Click the "Set options for the test case" button (with a monkey wrench and a screwdriver)
- Set a timeout in milliseconds, for example "5000"
- Click "OK"
4. How to configure the test request
- Double-click the "getTripPrice" test request
- Edit the request to ask for the price of the following trip : from Berlin to Paris, for 2 adults, lasting 3 days, one room needed
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:trip="http://trip.price.service">
<soapenv:Header/>
<soapenv:Body>
<trip:getTripPrice>
<trip>
<adults>2</adults>
<duration>3</duration>
<from>Berlin</from>
<rooms>1</rooms>
<to>Paris</to>
</trip>
</trip:getTripPrice>
</soapenv:Body>
</soapenv:Envelope>
- Click the "Submit request to specified endpoint URL" button (green triangle)
As a result, we get a SOAP fault, because the external services we need are not available. We will mock all of them.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Could not send Message.</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
5. How to mock external services
5.1 How to import the WSDL of the external service
- Right click on the projet and select "Add WSDL"
- Set the WSDL location, for example "/home/ubuntu/workspace/test-webservice-soapui/trip-price/trip-price-service/src/main/resources/wsdl/HotelPriceService.wsdl"
- Click "OK"
As a result, it adds a new interface to the project : "HotelPriceServiceFacadeServiceSoapBinding".
5.2 How to add a mock response to the test case
- Double click the test case to open the test case editor.
- Right click and select "Append Step" -> "Mock Response"
- Specify a name for the new step, for example "getRoomPrice Mock Response" and click "OK"
- Select the "getRoomPrice" operation of the "HotelPriceServiceFacadeServiceSoapBinding" interface.
- Set the port, here "8088"
- Set the path, here "/external-services-0.0.1-SNAPSHOT/webservices/HotelPriceService"
- Click "OK"
- Edit the generated response, to return a price, for example "55.0"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:hot="http://external.services/hotel">
<soapenv:Header/>
<soapenv:Body>
<hot:getRoomPriceResponse>
<return>55.0</return>
</hot:getRoomPriceResponse>
</soapenv:Body>
</soapenv:Envelope>
- Set the start step of the mock response in the "MockResponse Properties" tab to "getTripPrice". So the mock service will start listening to requests as soon as the getTripPrice request is send.
5.3 How to add assertions on the getRoomPrice request
Here we can check the request schema and the parameter values
We launch the test case, to trigger a request to the mock service, it will make assertion configuration easier.
- Click the "Runs this testcase" button (green triangle) in the test case editor
You should see a request in the mock response editor.
- Click the "Assertions" tab
- Click the "Adds an assertion to this item" button
- Select "Schema Compliance" and click "OK"
- Check the definition url to validate by and click "OK"
The assertion should appear as valid.
5.4 Now we do the same for FlightPriceService.
- Right click on the projet and select "Add WSDL"
- Set the WSDL location, for example "/home/ubuntu/workspace/test-webservice-soapui/trip-price/trip-price-service/src/main/resources/wsdl/FlightPriceService.wsdl"
- Click "OK"
As a result, it adds a new interface to the project : "FlightPriceServiceFacadeServiceSoapBinding".
- Double click the test case to open the test case editor.
- Right click and select "Append Step" -> "Mock Response"
- Specify a name for the new step, for example "getFlightPrice Mock Response" and click "OK"
- Select the "getFlightPrice" operation of the "FlightPriceServiceFacadeServiceSoapBinding" interface.
- Set the port, here "8088"
- Set the path, here "/external-services-0.0.1-SNAPSHOT/webservices/FlightPriceService"
- Click "OK"
- Edit the generated response, to return a price, for example "49.99"
- Set the start step of the mock response in the "MockResponse Properties" tab to "getTripPrice". So the mock service will start listening to requests as soon as the getTripPrice request is send.
- Click the "Runs this testcase" button (green triangle) in the test case editor
You should see a request in the mock response editor.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:getFlightPrice xmlns:ns2="http://external.services/flight">
<from>Berlin</from>
<to>Paris</to>
</ns2:getFlightPrice>
</soap:Body>
</soap:Envelope>
- Click the "Assertions" tab
- Click the "Adds an assertion to this item" button
- Select "Schema Compliance" and click "OK"
- Check the definition url to validate by and click "OK"
The assertion should appear as valid.
- Click the "Adds an assertion to this item" button
- Select "XPath Match" and click "OK", this opens the XPath Match configuration window
- Click "Declare" to generate declarations automatically
declare namespace soap='http://schemas.xmlsoap.org/soap/envelope/'; declare namespace ns2='http://external.services/flight';
- Query the "from" field //ns2:getFlightPrice/from
- Set the expected result, in this example "Berlin"
- Click "Save"
The assertion should appear as valid.
Do the same for the "to" field and check it is Paris.
6 How to check the response
Run the test case, it should now be successful.
- Open the test request editor
- Click the "Assertions" tab
- Add a "Schema Compliance" assertion, to check the response schema
- Add a "Not SOAP Fault" assertion, to check the response is not a SOAP fault
- Add a "SOAP Response" assertion, to check the response is a SOAP response
- Add a "XPath Match" assertion to check the price
XPath Expression
declare namespace soap='http://schemas.xmlsoap.org/soap/envelope/'; declare namespace ns2='http://trip.price.service'; //ns2:getTripPriceResponse/return
Expected Result : 264.98
(3 * 1 * 55 + 2 * 49.99)
Now your test case is complete.
Related topics:
"Web service testing with soapUI: exceptions""Integration of soapUI tests in maven build lifecycle"
Technorati Tags: soapUI test web service

This was VERY helpful. Testing SOAP interfaces is a bit tricky, and this helped me validate a third party SOAP web service in less than half an hour from downloading soapUI, installing it, and testing. Nice.