Assertions & Dot-Paths

Assertions are the core validation engine of Gherkio. Placed inside the expect block of any step, assertions inspect, type-check, and validate the returned HTTP response status, headers, body structure, and even embedded JWT tokens.


🏗️ The Anatomy of expect

The expect block defines a set of strict contracts that the HTTP response must satisfy. Gherkio evaluates these assertions sequentially upon request completion.

⚡ Fail-Fast Execution

Gherkio implements a strict fail-fast design.

  • Assertions are checked in the order they are written.
  • If any single assertion fails, step execution is immediately aborted, the entire scenario is marked as failed, and any subsequent steps (such as database queries or follow-up requests) are skipped.
  • This prevents cascading failures, reduces network bandwidth waste, and keeps your test reports clean and focused on the root cause.

🧭 Dot-Path Prefixes & Resolvers

Gherkio uses a declarative dot-notation system to target specific parts of the response. The prefix of the path determines which resolver is invoked:

Path PrefixTarget ComponentKey Case-SensitivityPurposeExample
statusHTTP Status CodeN/AValidates the exact status codestatus: 200
body.<path>JSON Response BodyCase-SensitiveTraverses response JSON structuresbody.user.email: email
headers.<key>HTTP Response HeadersCase-InsensitiveInspects response headersheaders.content-type: contains json
jwt.<claim>Decoded JWT payloadCase-SensitiveAutomatically decodes tokens and inspects claimsjwt.role: admin
schemaJSON Schema ContractN/AEnforces a full JSON schema contractschema: users/create
timing.durationRound-trip request timeN/AAsserts latency SLA/performancetiming.duration: lte 500ms

1. Response Body Traversal (body.<path>)

The body. prefix targets the response body payload (expected to be JSON). Gherkio traverses nested properties, list indices, and dictionary keys.

Key Syntax Features:

  • Nested Navigation: Use dots to descend into maps (e.g., body.user.profile.firstName).
  • Array Indexing: Use brackets to access specific list items (e.g., body.items[0].id).
  • Case Sensitivity: JSON keys are strictly case-sensitive. body.userId is different from body.userid.

💡 Intelligent Spelling Suggestion Engine

If you write an assertion for a path that does not exist in the response payload, Gherkio's smart diff engine doesn't just fail; it analyzes the keys that were actually present in the response and prints helpful spelling suggestions in your terminal:

✗ timing.duration = lte 500ms (actual: 312ms)
✗ body.accessToken = exists
  Reason: field 'accessToken' not found in response body
  Suggestions:
    - message
    - statusCode

2. HTTP Headers (headers.<key>)

The headers. prefix allows you to inspect HTTP response headers.

Key Syntax Features:

  • Case-Insensitivity: In accordance with the HTTP RFC, header lookups are fully case-insensitive. Gherkio normalizes these keys internally, meaning headers.content-type, headers.Content-Type, and headers.CONTENT-TYPE are completely equivalent.
  • Multi-Value Headers: If a header has multiple values, Gherkio normalizes them to a single comma-separated string for easy assertion matching (e.g. headers.cache-control: contains no-cache).

3. Automatic JWT Claim Decoding (jwt.<claim>)

One of Gherkio's most powerful native features is the automatic discovery and decoding of JSON Web Tokens.

How It Works:

  1. When a request completes, Gherkio scans the response body for standard JWT field names:
    • token
    • accessToken
    • access_token
    • idToken
    • id_token
  2. If any of these fields are present and contain a valid base64-encoded, three-part JWT structure (header.payload.signature), Gherkio automatically decodes the payload section into memory.
  3. You can assert directly against claims in the payload using the jwt. prefix.

Example:

If the server returns:

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4iLCJleHAiOjE3MTY4ODQwMDB9.sig"
}

You can immediately assert:

expect:
  jwt.sub: "123"
  jwt.role: "admin"
  jwt.exp: datetime  # Asserts that the expiration claim is present

4. JSON Schema Contracts (schema)

For absolute validation of deep objects and API payloads, individual key assertions can become unwieldy. The schema directive enforces full, deep-object JSON Schema validation.

Syntax:

expect:
  schema: path/to/schema

How It Works:

  • Path Resolution: Gherkio looks for the schema file under your project's configured schemas directory (usually .gherkio/schemas/). It automatically appends .yaml, .yml, or .json to locate the file (e.g., schema: users/profile resolves to .gherkio/schemas/users/profile.yaml).
  • Negation (not): You can assert that a response does not match a schema by prefixing the value with not (e.g. schema: not errors/validation). This is ideal for testing error states where a validation error structure should not be returned.

5. Response Timing Assertions (timing.duration)

To enforce service-level agreements (SLAs) or target performance constraints, Gherkio includes a native latency-checking resolver:

  • Syntax: timing.duration: <comparison-matcher> <duration>
  • Allowed Matchers: Supports comparison matchers like lte, lt, gte, and gt.
  • Duration Syntax: A numeric value followed by ms (milliseconds) or s (seconds).
expect:
  status: 200
  timing.duration: lte 500ms   # Step fails if request takes longer than 500ms

🔄 Dynamic Variables in Assertions

To avoid hardcoding values and to support end-to-end testing, Gherkio supports dynamic variable lookup directly in your assertion values.

1. Interpolating Saved & Global Variables ($<variable>)

Expected values inside the expect block are fully evaluated through the variable interpolation engine. This includes:

  • Saved Variables: Values extracted from previous steps using the save block (e.g. $userId).
  • Environment & Credentials Variables: Values loaded from the current environment YAML or credentials accounts (e.g. $accounts.admin.email).
steps:
  - request:
      method: POST
      url: /v1/users
      body:
        email: "bob@example.com"
    save:
      newUserId: body.id

  - request:
      method: GET
      url: /v1/users/$newUserId
    expect:
      status: 200
      # Validates that the returned ID matches the ID saved from the previous step
      body.id: $newUserId
      # Validates against a global test account email from your environment config
      body.invitedBy: $accounts.admin.email

2. Current Request References (request.body.<field>)

When verifying creation or update steps, you often want to assert that the response body contains the exact same data sent in the request. You can refer to the current request's body fields dynamically using the request.body. prefix.

steps:
  - request:
      method: POST
      url: /v1/products
      body:
        title: "Mechanical Keyboard"
        price: 99.99
    expect:
      status: 201
      # Asserts that the response payload echoes back the sent request fields dynamically
      body.data.title: request.body.title
      body.data.price: request.body.price

🗄️ Collection & Array Assertions

Gherkio provides advanced built-in collection resolvers to perform validations across lists without writing loops.

1. Array Size Validation (count(<array-path>))

Asserts the exact length of a list returned in the response.

  • Exact Count: count(body.items): 5
  • Comparators: Append .gt, .gte, .lt, or .lte suffixes to the path to perform numeric bounds checks:
    • count(body.items).gte: 1 — Asserts that the array is not empty (has at least 1 item).
    • count(body.items).lt: 10 — Asserts that the array contains fewer than 10 items.

[!NOTE] Unlike standard body paths, count() paths resolve directly against the parsed response body root. The body. prefix inside the parenthesis is optional, but supported.


2. Universal Collection Asserts (all(<array-path>))

Asserts that every single element in a list satisfies a specific condition.

  • Equality Check: all(body.items.status): active — Asserts that every item in the items list has a status equal to "active".
  • Matcher Assertion: all(body.items.price): number — Asserts that every item in the items list has a price field that is a number.

🌟 Comprehensive Real-World Example

Below is a complete, production-grade Gherkio test step demonstrating status codes, headers, nested body lookups, collection checks, automatic JWT assertions, and schema contract verification running side-by-side:

scenario: Complete API contract and session validation
steps:
  - request:
      method: POST
      url: /v1/auth/session
      body:
        username: "admin"
        device: "laptop"
    expect:
      # 1. Enforce exact HTTP status code
      status: 201

      # 2. Case-insensitive header checks
      headers.Content-Type: contains json
      headers.x-transaction-id: exists

      # 3. Deep-path JSON body validations using matchers & dynamic variables
      body.session.id: uuid
      body.session.metadata.device: request.body.device     # Dynamic reference to current request payload
      body.session.metadata.tenant: $tenantId               # Interpolates variable saved from previous steps
      body.session.active: true

      # 4. Collection checks with comparison bounds
      count(body.session.permissions).gte: 3
      all(body.session.permissions): string

      # 5. Automatic JWT discovery and claims validation
      jwt.role: "administrator"
      jwt.sub: uuid

      # 6. Full API schema validation (located at .gherkio/schemas/auth/session-response.yaml)
      schema: auth/session-response