XML steps parse an XML document - loaded from a file or supplied inline in the scenario - and assert against it with XPath: element existence, text equality and containment, attribute values, element counts, and namespace declarations. They are aimed at integration teams validating RSS/Atom feeds, sitemaps, SOAP responses, and any XML API payload, without leaving Gherkin.
The workflow is always two-phase: first a Given step sets the response content (from a file under tests/assets/ or from a doc string), then any number of Then assertions run against the parsed document. If an assertion runs before content is set, it fails immediately with No XML response has been set rather than a cryptic null error.
Parsing requires an XML DOM parser - install @xmldom/xmldom (the older xmldom package also works). XPath evaluation uses the optional xpath npm module when installed, giving you full XPath 1.0 (//entry, predicates like /feed/entry[2], attribute tests). Without it, a built-in fallback handles simple absolute child paths such as /root/item - so for anything beyond straight parent/child chains, install xpath.
Quick reference
| Step | What it does |
|---|---|
Given the response content from the file "feed.xml" | Load XML response content from a file under tests/assets/. |
Given the response content is the following: | Set XML response content inline from a Gherkin doc string. |
Then the response should be in XML format | Assert the loaded response parses as XML. |
Then the response should not be in XML format | Assert the loaded response is not XML. |
Then the XML element "/root/item" should exist | Assert at least one element matches an XPath. |
Then the XML element "/root/missing" should not exist | Assert no element matches an XPath. |
Then the XML element "/root/item" should be equal to "Hello" | Assert an element's text equals a value (trimmed). |
Then the XML element "/root/item" should not be equal to "Bye" | Assert an element's text is not equal to a value. |
Then the XML element "/root/item" should contain "Hello" | Assert an element's text contains a substring. |
Then the XML element "/root/item" should not contain "Error" | Assert an element's text does not contain a substring. |
Then the XML attribute "id" on element "/root/item" should exist | Assert an element has the named attribute. |
Then the XML attribute "deprecated" on element "/root/item" should not exist | Assert an element lacks the named attribute. |
Then the XML attribute "id" on element "/root/item" should be equal to "1" | Assert an attribute equals a value. |
Then the XML attribute "id" on element "/root/item" should not be equal to "0" | Assert an attribute is not equal to a value. |
Then the XML attribute "id" on element "/root/item" should contain "abc" | Assert an attribute contains a substring. |
Then the XML attribute "id" on element "/root/item" should not contain "old" | Assert an attribute does not contain a substring. |
Then the XML element "//entry" should have 5 elements | Assert exactly N elements match an XPath. |
Then the XML should use the namespace "http://www.w3.org/2005/Atom" | Assert the root declares a namespace URI. |
Then the XML should not use the namespace "http://example.com/legacy" | Assert the root does not declare a namespace URI. |
When I print last XML response | Print the raw XML response to stdout (debug aid). |
Steps in detail
Given the response content from the file "<file>"
Reads the file and parses it as XML. Relative paths resolve against the project's assets folder (tests/assets/ by default); absolute paths such as /tmp/sample.xml are used as-is. The raw text is also kept for the format checks and print last XML response.
Given the response content from the file "rss.xml"
Then the response should be in XML format
And the XML element "/rss/channel/title" should exist
Given the response content is the following:
Sets the XML inline from a Gherkin doc string - ideal for small, self-contained fixtures that live next to the assertions they support.
Given the response content is the following:
"""
<orders><order id="1"><status>shipped</status></order></orders>
"""
Then the XML element "/orders/order/status" should be equal to "shipped"
Then the response should be in XML format / should not be in XML format
The positive form re-parses the raw content and asserts a document element exists - a quick sanity gate before deeper XPath checks. The negated form passes when parsing fails or produces a parser error; use it to confirm an endpoint switched to JSON or plain text.
Given the response content is the following:
"""
{"hello": "world"}
"""
Then the response should not be in XML format
Then the XML element "<xpath>" should exist / should not exist
Existence checks: at least one node matches, or none does. With the xpath module installed you can use descendant axes (//status) and positional predicates (/feed/entry[2]).
Then the XML element "/order/customer" should exist
And the XML element "//deprecated" should not exist
Then the XML element "<xpath>" should be equal to "<text>" (and not be equal to)
Compares the first matching node's text content, trimmed, for exact equality. The positive form fails if the element does not exist; the negated form passes vacuously when no node matches, so pair it with a should exist check when presence matters.
Then the XML element "//status" should be equal to "shipped"
And the XML element "//status" should not be equal to "cancelled"
Then the XML element "<xpath>" should contain "<text>" (and not contain)
Substring variants of the text comparison - useful when the element mixes stable and dynamic text, such as a summary or description node.
Then the XML element "/feed/entry/summary" should contain "release"
And the XML element "/feed/entry/summary" should not contain "TODO"
Then the XML attribute "<attr>" on element "<xpath>" should exist / should not exist
Asserts the first node matching the XPath has (or lacks) the named attribute. The positive form fails first if the element itself is missing.
Then the XML attribute "version" on element "/rss" should exist
And the XML attribute "debug" on element "/rss" should not exist
Then the XML attribute "<attr>" on element "<xpath>" should be equal to "<value>" (and variants)
Four attribute-value assertions mirror the element-text ones: should be equal to, should not be equal to, should contain, and should not contain. Values compare as strings against getAttribute(). As with the element variants, the negated forms pass when the element is absent.
Then the XML attribute "version" on element "/rss" should be equal to "2.0"
And the XML attribute "href" on element "//link" should contain "example.com"
And the XML attribute "href" on element "//link" should not contain "tracking"
Then the XML element "<xpath>" should have <N> element(s)
Asserts exactly N nodes match the XPath - the XML analogue of a row count. 0 elements is a valid expectation and both element and elements are accepted.
Then the XML element "/feed/entry" should have 10 elements
And the XML element "//error" should have 0 elements
Then the XML should use the namespace "<uri>" / should not use the namespace
Checks the root element's attributes for an xmlns declaration whose value equals the given URI. Use it to pin a feed to Atom, a sitemap to the sitemaps.org schema, or a SOAP envelope to its namespace - and to prove a legacy namespace is gone.
Then the XML should use the namespace "http://www.w3.org/2005/Atom"
And the XML should not use the namespace "http://example.com/legacy"
When I print last XML response
Prints the most recently set raw XML to stdout (or (no response) when nothing is set). A debug aid that never fails - drop it into a scenario to see exactly what the assertions are running against.
Given the response content from the file "soap-response.xml"
When I print last XML response
Complete example
Feature: XML response assertions
Scenario: Validate an inline order feed
Given the response content is the following:
"""
<orders xmlns="http://example.com/orders">
<order id="1001">
<customer>Alice</customer>
<status>shipped</status>
</order>
<order id="1002">
<customer>Bob</customer>
<status>processing</status>
</order>
</orders>
"""
Then the response should be in XML format
And the XML should use the namespace "http://example.com/orders"
And the XML element "/orders/order" should have 2 elements
And the XML element "/orders/order/customer" should be equal to "Alice"
And the XML element "/orders/order/status" should contain "ship"
And the XML attribute "id" on element "/orders/order" should be equal to "1001"
And the XML element "/orders/refund" should not exist
When I print last XML response
Tips
- Install
@xmldom/xmldom(required) andxpath(recommended) as dev dependencies; withoutxpathonly simple/a/b/cchild paths are supported. - All text and attribute assertions read the first matching node; use positional predicates like
/orders/order[2](withxpathinstalled) to target later siblings. - The negated assertions (
should not be equal to,should not contain, attributeshould not exist) pass when the element itself is absent - add ashould existstep first when you need the element to be present. - For JSON APIs use the API steps; for YAML content see the YAML steps, which share the same file/doc-string loading pattern.