Skip to content

SOAP and XML load testing

SOAP (Simple Object Access Protocol) is an XML-based messaging protocol used in enterprise web services. Despite its age, many organizations still run SOAP-based services in finance, healthcare, and government systems. MaxoPerf can load-test SOAP services using the same HTTP mechanisms you use for REST — SOAP is HTTP POST with an XML body.

  • Read HTTP and REST load testing — SOAP load testing is a specialization of HTTP POST.
  • Obtain the WSDL for the service you are testing to understand the endpoint URL, action headers, and message structure.

SOAP requests are HTTP POST requests with:

  • Content-Type: text/xml; charset=utf-8 (SOAP 1.1) or application/soap+xml (SOAP 1.2).
  • A SOAPAction header identifying the operation (SOAP 1.1 only).
  • An XML SOAP envelope in the request body.

The response is always a 200 OK with an XML SOAP envelope body — or a 500 with a SOAP Fault envelope when the operation fails.

execution:
- concurrency: 20
ramp-up: 30s
hold-for: 5m
scenario: soap-account-service
scenarios:
soap-account-service:
default-address: https://services.example.com
requests:
- label: GetAccountBalance
url: /AccountService
method: POST
headers:
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://example.com/AccountService/GetAccountBalance"
body: |
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:acc="http://example.com/AccountService">
<soap:Header/>
<soap:Body>
<acc:GetAccountBalance>
<acc:AccountId>${account_id}</acc:AccountId>
</acc:GetAccountBalance>
</soap:Body>
</soap:Envelope>
assert:
- equals:
subject: http-code
value: '200'
- not-contains:
subject: body
value: '<soap:Fault>'
- label: TransferFunds
url: /AccountService
method: POST
headers:
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://example.com/AccountService/TransferFunds"
body: |
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:acc="http://example.com/AccountService">
<soap:Header>
<acc:AuthToken>${session_token}</acc:AuthToken>
</soap:Header>
<soap:Body>
<acc:TransferFunds>
<acc:FromAccount>${account_id}</acc:FromAccount>
<acc:ToAccount>TARGET-001</acc:ToAccount>
<acc:Amount>100.00</acc:Amount>
</acc:TransferFunds>
</soap:Body>
</soap:Envelope>
assert:
- not-contains:
subject: body
value: '<soap:Fault>'

SOAP errors return HTTP 200 with a <soap:Fault> element in the body (SOAP 1.1) or HTTP 500. Add a not-contains assertion for <soap:Fault> to catch application-level errors:

assert:
- not-contains:
subject: body
value: '<soap:Fault>'

k6 can send SOAP requests using its http.post() function with the appropriate headers and an XML body string:

import http from 'k6/http';
import { check } from 'k6';
const SOAP_URL = 'https://services.example.com/AccountService';
function buildGetBalanceEnvelope(accountId) {
return `<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:acc="http://example.com/AccountService">
<soap:Header/>
<soap:Body>
<acc:GetAccountBalance>
<acc:AccountId>${accountId}</acc:AccountId>
</acc:GetAccountBalance>
</soap:Body>
</soap:Envelope>`;
}
export const options = {
vus: 20,
duration: '5m',
};
export default function () {
const accountId = 'ACC-0001';
const res = http.post(
SOAP_URL,
buildGetBalanceEnvelope(accountId),
{
headers: {
'Content-Type': 'text/xml; charset=utf-8',
SOAPAction: '"http://example.com/AccountService/GetAccountBalance"',
},
tags: { name: 'GetAccountBalance' }, // shows in per-endpoint breakdown
}
);
check(res, {
'status 200': (r) => r.status === 200,
'no soap fault': (r) => !r.body.includes('<soap:Fault>'),
'balance present': (r) => r.body.includes('<acc:Balance>'),
});
}

SOAP 1.2 uses a different namespace and content type:

AspectSOAP 1.1SOAP 1.2
Envelope namespacehttp://schemas.xmlsoap.org/soap/envelope/http://www.w3.org/2003/05/soap-envelope
Content-Typetext/xml; charset=utf-8application/soap+xml; charset=utf-8
SOAPAction headerRequiredOptional (moved to Content-Type parameter)
Fault element<soap:Fault><soap12:Fault>

Update your assertions accordingly:

assert:
- not-contains:
subject: body
value: '<soap12:Fault>' # SOAP 1.2 fault element name

For tests that need different account IDs, transaction amounts, or user credentials per VU, use a CSV data entity in MaxoPerf:

scenarios:
parameterized-soap:
data-sources:
- path: accounts.csv
variable-names: account_id, session_token
requests:
- label: GetBalance
url: /AccountService
method: POST
headers:
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://example.com/AccountService/GetAccountBalance"
body: |
<?xml version="1.0"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetAccountBalance xmlns="http://example.com/AccountService">
<AccountId>${account_id}</AccountId>
</GetAccountBalance>
</soap:Body>
</soap:Envelope>

See Cookbook: CSV data-driven test for the full CSV setup.

SOAP results appear in the standard HTTP result view. Label each operation with label: (Taurus) or tags: { name: '…' } (k6) so the per-endpoint breakdown shows distinct rows for each SOAP operation rather than all requests lumped under the /AccountService URL.

Watch for:

  • Error rate spike — often indicates <soap:Fault> responses; check the Log tab for response bodies.
  • Latency increase — XML parsing is more CPU-intensive than JSON; latency may increase under high concurrency as the server’s XML parser becomes a bottleneck.

Do:

  • Assert for <soap:Fault> in the response body, not just HTTP status — many SOAP faults are HTTP 200.
  • Label each SOAP operation distinctly so the MaxoPerf per-endpoint panel shows meaningful breakdown.
  • Use MaxoPerf secrets for auth tokens that appear in the SOAP header.

Don’t:

  • Use XML entities that could trigger XXE parsing — MaxoPerf’s own bundle assembler rejects unsafe paths, but the target SOAP service may be vulnerable.
  • Include real customer account IDs or financial data in uploaded test files — use synthetic test data.