The YAML step group parses and asserts against YAML response bodies: configuration files, Kubernetes manifests, OpenAPI definitions, Helm output, CI pipelines - anything your suite needs to validate as structured YAML rather than as a rendered page. It mirrors the XML step group where that makes sense (path-based existence, equality, contains, count, and "namespace" checks) and adds YAML-native concepts: multi-document streams separated by ---, type-of assertions, numeric comparisons, array-of-objects matching, JSON Schema validation, key-set assertions, and diff-versus-expected with ignorable keys.
Content is loaded either from a file under tests/assets/ (or an absolute path) or inline from a Gherkin doc string. Once loaded, every assertion runs against the active document - for multi-document streams the first document is active by default and you switch with Given the active YAML document is N (1-based, matching how Helm and kubectl users count documents).
Paths use a JSON Pointer (RFC 6901) flavored syntax: segments are separated by /, and numeric segments index sequences, so /spec/containers/0/image reaches the first container's image. The bracket form /items[0]/name is also accepted. These steps run entirely in Node - no browser involved - so they pair naturally with the API and REST step groups for config and infrastructure QA from the same suite. The parser is js-yaml; schema validation uses ajv with ajv-formats (both are optional dependencies - the step tells you the exact npm i command if one is missing).
Quick reference
| Step | What it does |
|---|---|
Given the YAML response content from the file "config.yml" | Load YAML from a file under tests/assets/ (or an absolute path). |
Given the YAML response content is the following: | Set YAML inline from a doc string; supports multi-document streams. |
Given the active YAML document is 1 | Switch the active document in a multi-document stream (1-based). |
Then the YAML response should have 1 document | Assert the stream has exactly N documents. |
Then the response should be in YAML format | Assert the loaded content parses as YAML. |
Then the response should not be in YAML format | Assert the loaded content is NOT valid YAML. |
Then the YAML should have no duplicate keys | Assert no duplicate keys anywhere in the document. |
Then the YAML element "/name" should exist | Assert a node exists at a path. |
Then the YAML element "/missing" should not exist | Assert no node exists at a path. |
Then the YAML element "/name" should be equal to "example" | Assert a node's value equals an expected value. |
Then the YAML element "/name" should not be equal to "Other" | Assert a node's value differs from a value. |
Then the YAML element "/name" should contain "Web" | Assert a node's serialized value contains a substring. |
Then the YAML element "/name" should not contain "Error" | Assert a node's serialized value lacks a substring. |
Then the YAML attribute "id" on element "/items/0" should exist | Assert a node has a direct child key. |
Then the YAML attribute "deprecated" on element "/items/0" should not exist | Assert a node lacks a direct child key. |
Then the YAML attribute "id" on element "/items/0" should be equal to "1" | Assert a child key's value equals a value. |
Then the YAML attribute "id" on element "/items/0" should not be equal to "0" | Assert a child key's value differs from a value. |
Then the YAML attribute "id" on element "/items/0" should contain "abc" | Assert a child key's value contains a substring. |
Then the YAML attribute "id" on element "/items/0" should not contain "old" | Assert a child key's value lacks a substring. |
Then the YAML element "/items" should have 5 elements | Assert a sequence or mapping has exactly N entries. |
Then the YAML value at "/spec/replicas" should be of type "integer" | Assert a node's type (string, integer, number, boolean, array, object, null). |
Then the YAML value at "/labels" should be empty | Assert a node is empty ("" / [] / {} / null). |
Then the YAML value at "/items" should not be empty | Assert a node is non-empty. |
Then the YAML value at "/spec/replicas" should be greater than 0 | Numeric greater-than comparison. |
Then the YAML value at "/spec/replicas" should be greater than or equal to 1 | Numeric greater-or-equal comparison. |
Then the YAML value at "/spec/replicas" should be less than 100 | Numeric less-than comparison. |
Then the YAML value at "/spec/replicas" should be less than or equal to 10 | Numeric less-or-equal comparison. |
Then the YAML value at "/spec/replicas" should be between 1 and 10 | Numeric inclusive-range comparison. |
Then the YAML array at "/spec/containers" should contain an item where "/name" is "nginx" | Assert some array item has a sub-key equal to a value. |
Then the YAML array at "/spec/containers" should contain no item where "/image" is "alpine:latest" | Assert no array item has a sub-key equal to a value. |
Then every item in "/spec/containers" should have key "image" | Assert every array item has a given key. |
Then the YAML keys at "/metadata" should be exactly "name, namespace, labels" | Assert a mapping's keys exactly match a list (no extras). |
Then the YAML at "/metadata" should have keys "name, namespace" | Assert required keys are present (extras allowed). |
Then the YAML should match JSON Schema "schemas/deployment.json" | Validate the active document against a JSON Schema file. |
Then the YAML should equal the file "expected.yaml" ignoring keys "/metadata/resourceVersion" | Deep-equal against a golden file, stripping listed paths first. |
Then the YAML should use the namespace "v1" | Assert the document declares a namespace value. |
Then the YAML should not use the namespace "v0" | Assert the document does NOT declare a namespace value. |
When I print last YAML response | Print the raw YAML response to stdout (debug aid). |
Steps in detail
Given the YAML response content from the file "config.yml"
Loads YAML from a file. Relative filenames resolve under the world's assetsFolder (default ./tests/assets); absolute paths are used as-is. Multi-document streams (--- separators) are parsed with loadAll: the first document becomes the active one, and you can switch later with the active-document step.
Given the YAML response content from the file "k8s-deployment.yaml"
Then the response should be in YAML format
And the YAML element "/kind" should be equal to "Deployment"
Given the YAML response content is the following:
Sets YAML inline from a Gherkin doc string - handy for small fixtures that should live next to the assertions that use them. Also supports multi-document streams.
Given the YAML response content is the following:
"""
name: example
version: 1.0.0
"""
Then the YAML element "/name" should be equal to "example"
Given the active YAML document is 1 / Then the YAML response should have 2 documents
Multi-document controls. The index is 1-based to match how Helm and kubectl get -o yaml users describe documents; an out-of-range index fails with a friendly message telling you how many documents exist. The count step asserts the stream length exactly.
Then the YAML response should have 2 documents
And the YAML element "/kind" should be equal to "ConfigMap"
Given the active YAML document is 2
Then the YAML element "/kind" should be equal to "Service"
Then the response should (not) be in YAML format
Well-formedness checks: the positive form fails with the parser's error message if the raw content does not parse; the negative form passes when parsing throws.
Given the YAML response content from the file "openapi.yaml"
Then the response should be in YAML format
Then the YAML should have no duplicate keys
Re-parses the raw content with js-yaml's onWarning hook (duplicate keys raise warnings under the default schema) and fails listing every duplicate found. Useful as a lint gate on hand-edited config.
Given the YAML response content from the file "config.yml"
Then the YAML should have no duplicate keys
Then the YAML element "..." should (not) exist / be equal to / contain
The core path assertions. exist checks a node resolves at the path; be equal to compares the node's text form (numbers and booleans are stringified, so replicas: 3 equals "3"); contain does a substring check on the serialized value - for mappings and sequences that is the YAML dump, so the YAML element "/spec" should contain "replicas" works. The negative forms pass when the path does not resolve at all.
Then the YAML element "/kind" should exist
And the YAML element "/spec/replicas" should be equal to "3"
And the YAML element "/metadata/name" should contain "we"
And the YAML element "/legacy/field" should not exist
Then the YAML attribute "..." on element "..." should (not) exist / be equal to / contain
The "attribute" family treats a direct child key of a mapping the way XML steps treat attributes. Use / as the element path to address the document root. Six variants: exist, not exist, equal, not equal, contain, not contain.
Then the YAML attribute "kind" on element "/" should be equal to "Deployment"
And the YAML attribute "id" on element "/items/0" should exist
And the YAML attribute "deprecated" on element "/items/0" should not exist
Then the YAML element "/items" should have 5 elements
Counts entries at a path: array length for sequences, key count for mappings, 0 for scalars. Singular element and plural elements both parse.
Then the YAML element "/spec/containers" should have 2 elements
Then the YAML value at "..." should be of type "..."
Asserts the node's type. Accepted names: string, integer, number, boolean, array, object, null. Asking for number also accepts integers; asking for integer requires a whole number. This catches the classic YAML foot-gun of a value quietly changing type (e.g. replicas: "3" instead of replicas: 3).
Then the YAML value at "/replicas" should be of type "integer"
And the YAML value at "/paused" should be of type "boolean"
And the YAML value at "/tags" should be of type "array"
Then the YAML value at "..." should (not) be empty
Empty means: empty string, empty array, empty object, or null. The node must exist - a missing path fails rather than counting as empty.
Then the YAML value at "/tags" should be empty
And the YAML value at "/name" should not be empty
Numeric comparisons: greater than, greater than or equal to, less than, less than or equal to, between ... and ...
All five coerce the node to a number and fail with a friendly message if the value is not numeric or the path is missing. between is inclusive on both ends.
Then the YAML value at "/spec/replicas" should be greater than 0
And the YAML value at "/spec/replicas" should be less than or equal to 5
And the YAML value at "/timeout" should be between 5 and 60
Array matching: should contain an item where, should contain no item where, every item in ... should have key
These work on arrays of objects. The sub-path is evaluated relative to each item with the same /a/b syntax, and values compare by text form, so "/port" is "443" matches a numeric port: 443. The every-item step reports the index of the first offending item.
Then the YAML array at "/spec/containers" should contain an item where "/name" is "nginx"
And the YAML array at "/spec/containers" should contain no item where "/image" is "alpine:latest"
And every item in "/spec/containers" should have key "image"
Key-set assertions: keys at ... should be exactly, should have keys
Both take a comma-separated key list. be exactly compares the full key set order-insensitively and fails on extras or missing keys; have keys only requires the listed keys to be present. The path must resolve to a mapping.
Then the YAML keys at "/metadata" should be exactly "name, namespace, labels"
And the YAML at "/metadata" should have keys "name, namespace"
Then the YAML should match JSON Schema "schemas/deployment.json"
Validates the active document against a JSON Schema file (relative paths resolve under tests/assets/). Uses ajv with allErrors: true so the failure message lists every violation with its instance path, and bundles ajv-formats so format: date-time, email, uri, etc. work out of the box.
Given the YAML response content from the file "k8s-deployment.yaml"
Then the YAML should match JSON Schema "schemas/deployment.json"
Then the YAML should equal the file "expected.yaml" ignoring keys "..."
Golden-file diff: deep-equals the active document against an expected YAML file after removing the listed JSON Pointer paths from both sides. Typical use is ignoring volatile fields like /metadata/resourceVersion, /metadata/uid, or /status when diffing Kubernetes manifests. Pass an empty string to ignore nothing.
Then the YAML should equal the file "golden/deploy.yaml" ignoring keys "/status, /metadata/uid, /metadata/resourceVersion"
Then the YAML should (not) use the namespace "v1"
Mirrors the XML namespace step: checks whether any top-level scalar value equals the given string (so apiVersion: apps/v1 satisfies namespace "apps/v1"), falling back to a raw-text scan for non-mapping documents.
Then the YAML should use the namespace "apps/v1"
And the YAML should not use the namespace "v0"
When I print last YAML response
Prints the raw YAML response to stdout - a quick debug aid when an assertion fails and you want to see exactly what was loaded. Also accepts we as the subject.
When I print last YAML response
Complete example
Adapted from tests/features/yaml.feature in the webship-js suite:
Feature: YAML response assertions
Scenario: Validate a Kubernetes Deployment manifest
Given the YAML response content is the following:
"""
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
team: platform
spec:
replicas: 3
containers:
- name: nginx
image: nginx:1.25
- name: api
image: myapp:1.0
"""
Then the response should be in YAML format
And the YAML should have no duplicate keys
And the YAML element "/kind" should exist
And the YAML element "/spec/replicas" should be equal to "3"
And the YAML value at "/spec/replicas" should be between 1 and 10
And the YAML element "/spec/containers" should have 2 elements
And the YAML array at "/spec/containers" should contain an item where "/name" is "nginx"
And every item in "/spec/containers" should have key "image"
And the YAML keys at "/metadata" should be exactly "name, labels"
And the YAML attribute "kind" on element "/" should be equal to "Deployment"
Scenario: Multi-document streams
Given the YAML response content is the following:
"""
apiVersion: v1
kind: ConfigMap
---
apiVersion: v1
kind: Service
"""
Then the YAML response should have 2 documents
And the YAML element "/kind" should be equal to "ConfigMap"
Given the active YAML document is 2
Then the YAML element "/kind" should be equal to "Service"
Tips
- Install the optional parser once per project:
npm i js-yaml. For schema validation addnpm i ajv ajv-formats. Steps fail with the exact install command if a dependency is missing. - Relative file arguments (content files, schemas, golden files) all resolve under
tests/assets/; setassetsFolderinworldParametersto change the base directory. - All comparisons are text-based on scalars, so quote expected numbers:
should be equal to "3". Use the type-of step when the distinction between3and"3"matters. - These steps mirror the XML step group; if your endpoint can serve both formats, the two groups read almost identically.