SOAP Testing Guide: Patterns That Actually Work in 2026
SOAP testing is different. XPath, XSDs, SOAP Faults. Here's how to do it without losing your weekends.
What makes SOAP testing different
SOAP testing looks like REST testing with a costume on, until you hit the differences:
- Assertions are XPath, not JSON path.
- Responses validate against an XSD, not just shape.
- Errors come as SOAP Faults, not HTTP status codes.
- Contract drift is invisible unless you check the WSDL as part of the test run.
- Envelope boilerplate is verbose, so test data management matters more.
Treat these as features, not friction — they're why SOAP testing can be more rigorous than REST testing once set up properly.
The happy path
/soapcurl -X POST 'https://demo.totalshiftleft.ai/soap' \
-H 'Content-Type: application/json' \
-d '"<?xml version=\"1.0\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><GetUser xmlns=\"http://demo.totalshiftleft.ai/users\"><Id>1</Id></GetUser></soap:Body></soap:Envelope>"'A successful SOAP response comes back 200 OK with an envelope containing a response body element. Your assertions should cover:
- HTTP status is 200 (not 500 — a SOAP Fault is often 500 even when the service is "working as designed").
- The response is a valid SOAP envelope (starts with
<soap:Envelope>). - The expected response element exists (
//tns:GetUserResponse). - Key fields are present and have expected types (e.g.,
//User/Emailmatches an email regex). - The response validates against the XSD (strict mode).
The fault path
/soapcurl -X POST 'https://demo.totalshiftleft.ai/soap' \
-H 'Content-Type: application/json' \
-d '"<?xml version=\"1.0\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><GetUser xmlns=\"http://demo.totalshiftleft.ai/users\"><Id>99999</Id></GetUser></soap:Body></soap:Envelope>"'A SOAP Fault looks like:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>User not found</faultstring>
<detail>
<errorCode>USER_NOT_FOUND</errorCode>
<userId>99999</userId>
</detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>
Assertions:
- HTTP status is 500 (standard for SOAP Faults — yes, really, even for client errors).
//soap:Faultexists.faultcodematches expected (soap:Clientfor bad inputs,soap:Serverfor server errors).faultstringcontains the expected human message (loose match; don't assert exact copy).//detail/errorCodematches the structured code (this is what clients should branch on, not the faultstring).
XPath basics for testers
You don't need to master XPath. Six operators cover 90% of SOAP assertions:
| XPath | Means |
|---|---|
/ | Root element |
// | Anywhere in the document |
* | Any element |
[@attr='x'] | With attribute attr equal to x |
[n] | The nth child (1-indexed) |
text() | The text node of an element |
Examples:
//User/Email/text()— the email text inside any User element.//soap:Fault/faultcode/text()— the fault code.//Order[@status='paid']— orders where status attribute is paid.count(//User)— how many User elements are there.
Validate against the XSD
Shape-check + type-check in one step. Every mature XML library supports it:
from lxml import etree
xsd = etree.XMLSchema(etree.parse("user.xsd"))
doc = etree.fromstring(response_body)
xsd.assertValid(doc) # raises if invalid
What XSD validation catches that XPath misses:
- Wrong types (
"abc"where anxsd:intis expected). - Out-of-range values (
ageisxsd:intwith min 0 —-5fails). - Missing required fields that happen to be empty strings.
- Enumeration mismatches (
"YELLOW"when enum isRED|GREEN|BLUE).
This is table stakes for SOAP testing. If your suite only does XPath, you're missing 40% of the contract.
Contract testing — catch WSDL drift
The WSDL is a living document. When the server changes, so does the WSDL. Your test suite should:
- Snapshot the WSDL when tests pass (commit
fixtures/service.wsdl). - On each run, download the live WSDL.
- Diff against the snapshot.
- Fail the build if the diff isn't whitelisted.
This catches silent schema changes — the #1 cause of SOAP integration breakage.
Realistic test data
SOAP envelopes are long. Don't hand-write them in test code. Two patterns:
Template + parameters: one envelope file with {{id}} placeholders, rendered per test. Keeps test cases readable.
Generated from WSDL: tools like ShiftLeft, SOAPUI, or ReadyAPI generate the envelopes for you from the WSDL and fill in sample or fuzzed values. For any non-trivial SOAP service, this is the only sustainable approach.
A complete test matrix
For every operation in the WSDL, cover:
- Happy path — valid input, assert response shape + XSD validity.
- All declared faults — trigger each fault, assert faultcode and detail structure.
- Required fields — omit each, assert fault.
- Optional fields — include each, assert accepted.
- Type boundaries — min/max for numbers, length limits for strings, pattern mismatches.
- Enumeration violations — send an undeclared enum value, assert fault.
- Namespace violations — use wrong or missing namespace, assert fault.
- Oversized payloads — send a 10 MB body, assert graceful handling.
- SOAPAction mismatch — send a valid envelope with the wrong
SOAPActionHTTP header, document behavior. - Authentication — if WS-Security applies, test missing, expired, and malformed tokens.
A medium SOAP service (20 operations) needs 200+ test cases. Generating them from the WSDL turns weeks into hours.
Common mistakes
1. Asserting only on <Envelope> existing. Every SOAP response starts with <Envelope>, including SOAP Faults. This asserts nothing. Assert on the specific response element.
2. Treating HTTP 500 as "test failed". A SOAP Fault comes over HTTP 500. Your test should expect 500 on the negative path.
3. Hardcoded namespaces in XPath. Use namespace prefixes declared in your test runner, not string-literal namespaces — they'll change between environments.
4. Ignoring whitespace and ordering. XML can differ whitespace-wise without being semantically different. Use XML-aware comparison, not string equality.
5. Hitting production. SOAP services often have long-running side-effects (account creation, payment submission). Always hit a dedicated test environment. Use a recorded-response mock for repeatable unit tests.
What's next
You've covered SOAP end-to-end. Next up: the GraphQL testing guide — GraphQL-specific patterns like N+1 detection and contract testing over an evolving schema.
Related lessons
SOAP isn't dead — it runs banks, insurance, and government. Here's how it actually works.
WSDL is the WSDL — Web Services Description Language. It's the source of truth for every SOAP API. Here's how to read it.
The same operation in three protocols. Pick the right one for the job — and test it right.