Testing and debugging SOAP services
Generate a request from the WSDL, look at the XML that actually went on the wire, and assert on faults in automated tests.
Getting a request to run
# Record the exchange for a real service, then read the XML, not the framework error
curl -s -o response.xml -D headers.txt -X POST "https://example.com/OrderService" \
-H 'Content-Type: application/soap+xml; charset=utf-8; action="urn:example:orders/PlaceOrder"' \
--data-binary @request.xml
head -1 headers.txt
xmllint --format response.xml | head -40
# Validate the response against the schema from the WSDL before reading it
xmllint --noout --schema order.xsd response.xml && echo "schema valid"
# Replay the exact bytes through a recording proxy to compare a working and failing call
mitmdump -w flow.dump
mitmproxy -r flow.dump- SoapUI and ReadyAPI generate a skeleton request from the WSDL, including a mock service you can run to test the client before the server exists.
- Postman can import a WSDL and produce an example request, which is enough for a quick check.
- Always inspect the raw XML. Framework error messages describe a symptom several layers away from the actual malformed element.
- Test a deliberate fault path: send a request that must fail and assert on the fault code and detail, not just on an exception being thrown.
Automated tests that fail for the right reason
import requests
from lxml import etree
NS = {"s": "http://www.w3.org/2003/05/soap-envelope"}
def call(body_xml: str) -> etree._Element:
r = requests.post(
"https://example.com/OrderService",
data=body_xml.encode("utf-8"),
headers={"Content-Type": ('application/soap+xml; charset=utf-8; '
'action="urn:example:orders/PlaceOrder"')},
timeout=10)
return etree.fromstring(r.content)
def test_unknown_sku_returns_a_fault():
resp = call("<PlaceOrder xmlns='urn:example:orders'><sku>NOPE</sku></PlaceOrder>")
fault = resp.find(".//s:Fault", NS)
assert fault is not None, "expected a fault, got a normal response"
assert (resp.findtext(".//s:Code/s:Value", namespaces=NS)
== "env:Sender")
assert "SKU" in resp.findtext(".//s:Reason/s:Text", namespaces=NS, default="")| Symptom | First thing to check |
|---|---|
| 401 or 403 with valid credentials | Whether WS-Security or the gateway, not application authentication, is rejecting |
| Cannot resolve the operation | The action URI and namespace, exactly as declared in the WSDL |
| Parse error on the client | Whether the response is a fault being parsed as a success message |
| Hangs then times out | A retry loop or an unbounded connection pool, not the service itself |
| Works with curl, fails in the framework | The framework is serialising an extra element or a different namespace prefix |
💡
Keep a captured request and response pair for every operation in the repository. When a partner upgrades their stack, the diff between the saved fixture and the new message pinpoints the change in seconds, and it doubles as documentation of the exact wire format.
FAQ
What replaced SoapUI for new projects?
Postman or a thin script wrapper over HTTP for one-off calls, and a real test suite for regression coverage. SoapUI remains useful mainly for WSDL-driven mock services.
How do I test a service I cannot call repeatedly?
Record responses through a proxy, then run tests against the recordings. Re-record on a schedule so schema drift is still detected.
Related
SOAP faults and error handling Modernising SOAP: wrapping and migrating to REST or gRPC
Last refreshed 2026-09-18.