YAML Steps

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

StepWhat 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 1Switch the active document in a multi-document stream (1-based).
Then the YAML response should have 1 documentAssert the stream has exactly N documents.
Then the response should be in YAML formatAssert the loaded content parses as YAML.
Then the response should not be in YAML formatAssert the loaded content is NOT valid YAML.
Then the YAML should have no duplicate keysAssert no duplicate keys anywhere in the document.
Then the YAML element "/name" should existAssert a node exists at a path.
Then the YAML element "/missing" should not existAssert 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 existAssert a node has a direct child key.
Then the YAML attribute "deprecated" on element "/items/0" should not existAssert 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 elementsAssert 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 emptyAssert a node is empty ("" / [] / {} / null).
Then the YAML value at "/items" should not be emptyAssert a node is non-empty.
Then the YAML value at "/spec/replicas" should be greater than 0Numeric greater-than comparison.
Then the YAML value at "/spec/replicas" should be greater than or equal to 1Numeric greater-or-equal comparison.
Then the YAML value at "/spec/replicas" should be less than 100Numeric less-than comparison.
Then the YAML value at "/spec/replicas" should be less than or equal to 10Numeric less-or-equal comparison.
Then the YAML value at "/spec/replicas" should be between 1 and 10Numeric 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 responsePrint 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 add npm 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/; set assetsFolder in worldParameters to 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 between 3 and "3" matters.
  • These steps mirror the XML step group; if your endpoint can serve both formats, the two groups read almost identically.

Back to all step groups