XML and SOAP
XML (Extensible Markup Language), defined by the W3C XML 1.0 Recommendation, stores data as text in named, nested elements such as <orderId>ORD-1042</orderId>. SOAP is a messaging protocol built on it: every request and response is an XML document called an envelope, and a WSDL file describes the service's operations and the XML each one accepts.
Example: to get an order's status, the client POSTs an envelope whose body holds GetOrderStatus and the order ID, and receives an envelope with the status or a SOAP fault saying what went wrong. Unlike REST, the operation is named inside the message, not by the method and the URI.
Why it matters
SOAP is common in integrations between organisations and in long-lived enterprise systems such as banking, payment, insurance, government and telecom interfaces, often behind a modern front end. A defect there can stop a whole business process, such as confirming a payment.
XML and SOAP also fail differently from JSON over REST. One unescaped & makes the whole message unreadable, an element in the wrong namespace is unknown although its name looks right, and SOAP 1.1 returns HTTP 500 even for bad input. Reading only status codes misses defects and invents false ones.
XML basics: well-formed, valid and namespaced
An element is a start tag, its content and an end tag, such as <total>249.90</total>; one with no content can be written <item/>. An attribute is a name="value" pair inside a start tag, such as currency="SAR" in the document below. A document has exactly one root element; every element is closed and properly nested, and names are case-sensitive, so <orderId> and <OrderId> are different elements. Attribute values are quoted, each attribute name appears once per element, and their order carries no meaning.
Following these rules makes a document well-formed; XML 1.0 makes any violation a fatal error, so the parser rejects the whole message instead of repairing it as browsers do with HTML. A valid document also matches a schema (XML 1.0 defines this for DTDs); web services use XML Schema (XSD) for required elements, order, types and allowed values.
- Escaping:
<and&must not appear raw in text; write<and&(XML also predefines>,"and'). A CDATA section,<![CDATA[...]]>, carries text without escaping. - Encoding: the optional declaration
<?xml version="1.0" encoding="UTF-8"?>names it; bytes that do not match garble Arabic or Hindi names or get the message rejected. - Namespaces:
xmlns:ord="urn:example:orders"binds a prefix to a URI. An element is identified by URI plus local name, so the prefix does not matter, and the right name in the wrong namespace is unknown.
<?xml version="1.0" encoding="UTF-8"?>
<order id="ORD-1042" currency="SAR">
<customer>Smith & Sons</customer>
<item sku="A-100" qty="2"/>
<note><![CDATA[Leave at <b>reception</b>]]></note>
<total>249.90</total>
</order>The SOAP envelope and its HTTP binding
A SOAP message is an XML document with the root Envelope. The optional Header carries metadata such as a request ID or a security token; the mandatory Body carries the request or response element, or a fault. A header block marked mustUnderstand that the receiver cannot process must produce a MustUnderstand fault.
Requests usually go with POST to one endpoint URL shared by all operations. The two SOAP versions differ in the envelope namespace and the HTTP binding, so a request built for one version fails against a service that supports only the other.
SOAPAction, and the XML envelope's namespace sets the version, with an optional Header and a required Body naming the operation.HTTP request line and headers
Part 1:POST /services/orders HTTP/1.1 Host: api.example.com Part 2:Content-Type: text/xml; charset=utf-8 Part 3:SOAPAction: "urn:example:orders/GetOrderStatus"
XML body: the SOAP envelope
Part 4:<?xml version="1.0" encoding="UTF-8"?> Part 5:<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" Part 6:xmlns:ord="urn:example:orders"> Part 7:<soap:Header> <ord:RequestId>7f3c-0091</ord:RequestId> </soap:Header> Part 8:<soap:Body> Part 9:<ord:GetOrderStatus> Part 10:<ord:orderId>ORD-1042</ord:orderId> </ord:GetOrderStatus> </soap:Body> </soap:Envelope>
- 1Usually
POSTto one endpointEvery operation shares this URL; the element inBodynames the operation - 2SOAP 1.1 media typeSOAP 1.2 uses
application/soap+xml - 3
SOAPActionheader, SOAP 1.1 onlyThe client must send it, with the value from the WSDL binding; SOAP 1.2 uses anactionparameter instead - 4XML declarationOptional; names the character encoding
- 5
Envelopein the SOAP namespaceThe namespace sets the version; one the service does not support getsVersionMismatch - 6Service namespaceThe schema's
targetNamespace; the operation's elements must be in it - 7
Header, optionalMessage metadata such as a request ID; a block markedmustUnderstandmust be processed or answered with a fault - 8
Body, requiredThe request or response element, or aFault - 9Operation elementIts name and namespace select the operation
- 10Parameter checked against the XSDRequired; must match
ORD-plus 1 to 8 digits
- SOAP 1.1 (a W3C Note, 2000): namespace
http://schemas.xmlsoap.org/soap/envelope/,Content-Type: text/xml, and a mandatorySOAPActionheader whose value the WSDL gives for each operation. - SOAP 1.2 (a W3C Recommendation since 2003): namespace
http://www.w3.org/2003/05/soap-envelope,Content-Type: application/soap+xml, and the action, if any, as anactionparameter of that header.
Text version
POST /services/orders HTTP/1.1
Host: api.example.com
Content-Type: text/xml; charset=utf-8
SOAPAction: "urn:example:orders/GetOrderStatus"
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ord="urn:example:orders">
<soap:Header>
<ord:RequestId>7f3c-0091</ord:RequestId>
</soap:Header>
<soap:Body>
<ord:GetOrderStatus>
<ord:orderId>ORD-1042</ord:orderId>
</ord:GetOrderStatus>
</soap:Body>
</soap:Envelope>WSDL: the contract of a SOAP service
A WSDL (Web Services Description Language) document is the machine-readable contract of a SOAP service, by convention often published at the endpoint with ?wsdl appended. WSDL 1.1 (a 2001 W3C Note) is still widely used; WSDL 2.0 (2007) renames portType to interface and port to endpoint. WSDL 1.1 has the five top-level elements listed below.
The XSD in types is the most precise part of your test basis. In the schema at the end of this section, orderId is required (minOccurs defaults to 1) and must match ORD- plus 1 to 8 digits, since XSD patterns match the whole value. includeItems is optional and accepts only true, false, 1 or 0; xs:sequence fixes the order; targetNamespace is the namespace the request elements must use.
types: XML Schema definitions of the request, response and fault elements.message: the data each operation sends or receives.portType: the operations with their input, output and declared faults.binding: the SOAP version,documentorrpcstyle, and each operation'ssoapAction.service: the port's address, the URL you send requests to.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:example:orders"
elementFormDefault="qualified">
<xs:element name="GetOrderStatus">
<xs:complexType>
<xs:sequence>
<xs:element name="orderId">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:pattern value="ORD-[0-9]{1,8}"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="includeItems" type="xs:boolean" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>SOAP faults: errors with a structure
A request that cannot be processed gets a Fault element inside Body. In SOAP 1.1 it holds faultcode, faultstring, an optional faultactor and a detail element, which must be present when the contents of Body could not be processed and carries application-specific data such as the business faults declared in the WSDL. SOAP 1.2 calls them Code (with a Value and an optional Subcode), Reason, Node, Role and Detail, of which only Code and Reason are required. Assert the fault code first:
Client(1.1) orSender(1.2): the message is wrong — malformed, invalid or carrying a bad value — and fails again if resent unchanged.Server(1.1) orReceiver(1.2): the message was fine but processing failed, for example because a database was down. It may succeed later.VersionMismatch: an unsupported envelope namespace.MustUnderstand: a mandatory header block was not understood. SOAP 1.2 addsDataEncodingUnknown.- HTTP status: SOAP 1.1 requires
500for every fault,Clientfaults included; SOAP 1.2 uses400forSenderfaults and500for the rest. In SOAP 1.1 a500after bad input is therefore correct; the fault code decides.
HTTP/1.1 500 Internal Server Error
Content-Type: text/xml; charset=utf-8
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>orderId must match ORD- followed by 1 to 8 digits</faultstring>
<detail>
<ord:ValidationError xmlns:ord="urn:example:orders">
<ord:field>orderId</ord:field>
<ord:value>1042</ord:value>
</ord:ValidationError>
</detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>Worked example: testing GetOrderStatus
The order service runs SOAP 1.1 on a test environment the team owns. The tester derives checks from the WSDL and its schema, each with an expected result, and checks every hand-written envelope for well-formedness before sending it (the XML Validator checks well-formedness, not the XSD).
Responses are asserted with XPath, a query language for XML documents: //ord:status selects every status element in the namespace that the test tool binds to ord, whatever prefix the response itself uses. The tester never compares a response as text.
- Valid request for
ORD-1042: HTTP200and aGetOrderStatusResponsein the service namespace that validates against the XSD. - Unknown order
ORD-99999999: the fault the WSDL declares, such asOrderNotFound, not a200with an empty body. - Not well-formed, such as
</ord:orderID>closing<ord:orderId>: aClientfault, never an HTML page. Nofaultstringcontains stack traces, class names, SQL or file paths. - Invalid:
orderIdmissing,1042without the prefix,ORD-followed by no digits or by nine (just outside the 1 to 8 boundaries),includeItemsset toyes, or the elements swapped: aClientfault naming the field. An accepted one means the service does not validate input; ask whether that is intended. - Envelope: an envelope in the SOAP 1.2 namespace, sent with
text/xml, getsVersionMismatch; aSOAPActionthat does not match the body never runs a different operation. - A
<!DOCTYPE>before the envelope should be refused with a fault: both SOAP versions forbid a document type declaration in a message, and a parser that accepts one may be open to XML external entity (XXE) attacks. Run such checks only on systems you are authorised to test.
Common mistakes
- Judging a SOAP response by its HTTP status: in SOAP 1.1 a
500can be the right answer to bad input, and a200can carry an error returned as data. - Comparing XML as text or asserting on prefixes such as
ns2:; equivalent messages can differ in attribute order, indentation and prefixes. - Sending hand-written XML without checking that it is well-formed, then reporting the correct
Clientfault as a defect. - Testing only valid envelopes instead of breaking each required element, type, pattern and list of allowed values, and the element order.
- Assuming the server validates requests against the XSD. That is a configuration choice: send an invalid message and see.
The QA perspective
On a SOAP project, start from the WSDL as you would from an OpenAPI document: list the operations and declared faults, and read the XSD for required elements, types and limits. Agree early which SOAP version is used, whether requests are schema-validated, and which business errors are faults rather than normal responses.
Keep request envelopes as versioned test data, one per check. In a bug report include the endpoint, the SOAP version, the SOAPAction, the full request and response envelopes and the HTTP status: in XML, one wrong character or namespace is often the whole defect.
Practice exercise
Review five responses from a SOAP 1.1 service
A colleague sent five requests to the CreateCustomer operation of a SOAP 1.1 service on the test environment and noted what was sent and what came back. Decide for each run whether the behaviour is correct or a defect, and why.
Operation: CreateCustomer SOAP 1.1 test environment
Schema: name xs:string, maxLength 50, required
email xs:string, required
WSDL: declared fault DuplicateEmail
Run 1 sent: <cus:name>Smith & Sons</cus:name>, valid email
got: 200, <cus:name>Smith & Sons</cus:name> in the response
Run 2 sent: <cus:Email>sara@example.com</cus:Email> (no <cus:email>)
got: 200, customer C-381 created with no email
Run 3 sent: valid request, email already registered
got: 500, faultcode soap:Client, detail <cus:DuplicateEmail/>
Run 4 sent: valid request while the database is stopped
got: 500, faultcode soap:Client, faultstring "Invalid request"
Run 5 sent: envelope without </soap:Body>
got: 500, Content-Type text/html, page shows a Java stack trace- For each run, write the expected HTTP status, fault code and body from the schema, the WSDL and the SOAP 1.1 rules.
- Mark each run as correct or defect, and give each defect a one-line bug title.
Show the model answer
Run 1: correct. Expected 200 and the created customer: & is escaped as & in XML and came back unchanged; the stored name is Smith & Sons.
Run 2: defect. XML names are case-sensitive, so <cus:Email> is unknown and the required email is missing; 500 with a Client fault naming email was due, so the service is not validating against its schema. Title: "CreateCustomer creates a customer without the required email".
Run 3: correct. SOAP 1.1 returns 500 for every fault, and Client with the declared DuplicateEmail detail tells the caller what to fix.
Run 4: defect. The request was valid, so the expected answer is 500 with a Server fault; a Client fault tells callers to change a correct message and stops their retries. Title: "CreateCustomer returns a Client fault when the database is down".
Run 5: defect. Malformed XML should get 500 and a Client fault in a SOAP envelope sent as text/xml; an HTML page breaks SOAP clients and the stack trace leaks internals. Title: "CreateCustomer answers malformed XML with an HTML stack trace".
Frequently asked questions
Is SOAP still worth learning if I already know REST?
Yes, if you will test integrations between organisations or long-lived enterprise systems, where SOAP remains common. Most of what you learned for REST carries over — contracts, negative tests, precise bug reports — and SOAP adds strict XML, a formal WSDL contract and structured faults.
What is the difference between well-formed and valid XML?
Well-formed means the document follows XML's syntax rules: one root, closed and properly nested elements, quoted attributes, escaped special characters. Valid means it is well-formed and also matches a schema, such as the XSD in a WSDL. A parser must reject a document that is not well-formed; a document can be well-formed and still invalid.
Can I test a SOAP service with a REST tool?
Yes. A SOAP request is an HTTP POST with an XML body, so any tool that sends a raw body with custom headers works. You set Content-Type yourself and, for SOAP 1.1, SOAPAction. Dedicated SOAP tools add conveniences such as building requests from the WSDL.
How do I tell whether a service uses SOAP 1.1 or 1.2?
Look at the WSDL binding: a SOAP 1.1 binding uses the namespace http://schemas.xmlsoap.org/wsdl/soap/, a SOAP 1.2 binding http://schemas.xmlsoap.org/wsdl/soap12/. A service may offer both, with one port each. In a working request, the envelope namespace and the Content-Type (text/xml or application/soap+xml) tell you the same.
References
- Extensible Markup Language (XML) 1.0 (Fifth Edition)What makes an XML document well-formed: elements, attributes, entities, character encoding and the prolog.
- SOAP Version 1.2 Part 1: Messaging Framework (Second Edition)The SOAP 1.2 messaging framework: the XML envelope with an optional header and a mandatory body, the fault element, and the processing model for intermediaries.
- OWASP Cheat Sheet Series: Web Service SecuritySecuring SOAP and XML web services: transport security, message integrity, schema validation and XML-specific attacks.
- OWASP Cheat Sheet Series: XML External Entity PreventionWhy XML parsers must disable external entities and DTDs, and how to configure common parsers safely.
Practise with Mutqan tools
Send SOAP 1.1 / 1.2 envelopes with SOAPAction headers and view the formatted XML response.
Check XML well-formedness and report errors with line and column.
Pretty-print XML with configurable indentation, and minify it back.
Convert XML to JSON, preserving attributes, text nodes and repeated elements as arrays.
Go deeper
Your progress on this topic
Not started
Mark the topic complete when you have studied it and done the exercise.
Sign in to track your progress
Mark topics complete, continue where you left off and unlock specialisation exams.