Mocking SOAP web services with Imposter
We all know that integration testing is important. But when a third party service goes down and causes all your integration tests to fail you realise how brittle this can make your CI/CD pipeline. Sometimes you need to isolate yourself and create a mock of a SOAP web service.
Need a quick mock of a SOAP web service? Say hello to Imposter.
Using OpenAPI/Swagger? Check out the other article: Mocking APIs with OpenAPI and Imposter.
A simple web service
Let’s start with a simple web service. In keeping with the rules of the internet, this must involve cats. To keep things simple we’ll define a single endpoint for now, named /pets/
that will return a SOAP message like so:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2001/12/soap-envelope">
<env:Header/>
<env:Body>
<getPetByIdResponse xmlns="urn:com:example:petstore">
<id>3</id>
<name>string</name>
</getPetByIdResponse>
</env:Body>
</env:Envelope>
To represent this web service, we are going to use Imposter. Imposter is an open source tool for easily creating mocks. In this scenario we will use the SOAP plugin.
Note: if you already have a Swagger/OpenAPI specification, you want to use the OpenAPI plugin instead.
The configuration
Here is the configuration file for our endpoint:
# petstore-config.yaml
---
plugin: soap
wsdlFile: petstore.wsdl
Important: Your configuration file must be named with the suffix
-config.yaml
— for example:petstore-config.yaml
A few things to call out:
- We are using a WSDL file (
petstore.wsdl
) containing a simple web service. See the files on GitHub - The WSDL defines the service
PetService
at the SOAP endpoint/pets/
- It has one operation:
getPetById
- A request and response (‘input’ and ‘output’) message is defined using XSD
Start the mock
Now we’re going to start a mock server that you can call from your client code/web browser/CLI.
Preflight checks
- Ensure you have Docker installed and running.
- You should run the command in the next section from the directory containing your WSDL file and Imposter configuration file:
$ ls -l
-rw-r--r-- 1 pete staff 104 16 Sep 13:55 petstore-config.yaml
-rw-r--r-- 1 pete staff 998 16 Sep 13:55 petstore.wsdl
Let’s go!
Start the mock server as follows:
$ docker run -ti -p 8080:8080 \
-v $PWD:/opt/imposter/config \
outofcoffee/imposter-all
You should see log output including the lines:
Loading configuration file: /opt/imposter/config/petstore-config.yaml
Loaded 1 plugin configuration files from: [/opt/imposter/config]
...
Adding mock endpoint: tns:SoapBinding -> /pets/
Mock engine up and running on http://localhost:8080
Congratulations! 🎉 You now have a working mock server. Let’s test it.
Testing your mock
Let’s try a test from the command line:
Send a SOAP request to the /pets/
path defined in the configuration file to see the example response:
curl -X POST "http://localhost:8080/pets/" \
-H 'Content-Type: application/soap+xml' \
-d '<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2001/12/soap-envelope">
<env:Body>
<getPetByIdRequest xmlns="urn:com:example:petstore">
<id>3</id>
</getPetByIdRequest>
</env:Body>
</env:Envelope>'
Tip: don’t forget the trailing quote at the end of the string!
Here is the response:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2001/12/soap-envelope">
<env:Header/>
<env:Body>
<getPetByIdResponse xmlns="urn:com:example:petstore">
<id>3</id>
<name>string</name>
</getPetByIdResponse>
</env:Body>
</env:Envelope>
It works! Your mock is returning an example SOAP response at the path /pets/
, all from Imposter understanding the WSDL file.
Customising the response
It is often useful to customise the mock response, based on a given input, such as a property of the request. Let’s see how we can customise the payload in the SOAP body.
We will update our configuration file as follows:
plugin: soap
wsdlFile: petstore.wsdlresources:
- operation: getPetById
requestBody:
xPath: "/env:Envelope/env:Body/pets:getPetByIdRequest/pets:id"
value: "3"
xmlNamespaces:
env: "http://www.w3.org/2001/12/soap-envelope"
pets: "urn:com:example:petstore"
response:
staticFile: getPetByIdResponse.xml
Here we have added a configuration entry to look for a particular value in the XML body of the request. If an incoming request matches, Imposter returns the contents of an XML file, getPetByIdResponse.xml
, instead of the default response.
We can test this by re-sending our curl
request. This time, the contents of the XML file should be returned. This example illustrates how to use request matching to return a custom response file to control the exact content returned.
For completeness
The following example shows how you can simulate fault responses. Here, Imposter looks for a value of 99
in the request body and returns an HTTP 500 status code and the contents of a different file, fault.xml
.
# (previous content as above) - operation: getPetById
requestBody:
xPath: "/env:Envelope/env:Body/pets:getPetByIdRequest/pets:id"
value: "99"
xmlNamespaces:
env: "http://www.w3.org/2001/12/soap-envelope"
pets: "urn:com:example:petstore"
response:
staticFile: fault.xml
statusCode: 500
Summary
You now have a working mock server. Its endpoints are built from the WSDL file you provided. This means that as your WSDL changes Imposter will keep your mock in sync with the API definition.
If you update your configuration file or WSDL, just stop/start Imposter to see the changes.
Tip: To stop the running mock, just type CTRL+C in its terminal.
Customising your responses with scripts
Imposter is simple to learn, but is also very customisable. If you outgrow returning static example responses from your specification, you can use scripts, written in JavaScript or Groovy, to control the response your mock returns.
For more information on scripted responses, check out the documentation.
Looking for a hosted solution?
Do you want to try Imposter, but don’t want to run it yourself? Check out the hosted Imposter service at www.mocks.cloud
It’s free to try!
Example code
Get the example code on GitHub.