Skip to content

YAML Syntax Cheat Sheet

Quick reference for the TestLab YAML test authoring format (testlab: v1-alpha).


Document Types

Kind Purpose Required key
kind: tck Test Collection Kit — groups tests with shared config tests:
kind: test Single test case steps: (≥1 step)

TCK File Structure (index.yaml)

Only TCKs declare env:. Tests inherit it via namespace: matching.

YAML
kind: tck
testlab: v1-alpha
id: my-tck
namespace: my-namespace-v1.0
metadata:
  name: "My TCK"
  version: "1.0.0"
  description: "What this TCK certifies"
  dataspace_version: saturn
  standards:
    - id: CX-0135
      version: v3.1.0
  tags: [tag1, tag2]
env:
  variables:
    provider_url: ""
    timeout_seconds: 300
  services:
    - name: testlab_connector
      uses: service/connector_service
      with:
        base_url: ${{ env.provider_url }}
        management_path: /management/v3
        dsp_path: /api/v1/dsp/2025-1
        dataspace_version: ${{ metadata.dataspace_version }}
        auth:
          type: api_key
          api_key: "test-api-key"
          api_key_header: "X-Api-Key"
      returns:
        connector_service:
          type: class
          class: ConnectorService
  schemas:
    my_schema:
      file: my_schema.json
  testdata:
    sample_body:
      file: sample_body.json
tests:                       # ordered; entries are mappings, never bare strings
  - id: test_one.yaml        # the file name in tests/
    name: What test one proves
  - id: test_two.yaml
    name: What test two proves
    skippable: true          # optional; the operator may omit it via skip_tests

skippable defaults to false. The player rejects the whole run when skip_tests names a test that is unknown or not marked, so only what the author allowed can ever be skipped.


Test File Structure

Tests have NO env: block — they inherit everything from their parent TCK.

YAML
kind: test
testlab: v1-alpha
id: my-test
namespace: my-namespace-v1.0
metadata:
  name: "My Test"
  version: "1.0"
  description: "What this test validates"
setup: []
steps: []
teardown: []

Step Structure

YAML
- id: step_id
  uses: category/action
  name: "Human-readable label"
  with:
    param: value
    ref_param: ${{ steps.other_step.output }}
  returns:
    output_name:
      type: string
      class: SemanticClass
  validate:
    - uses: validate/assert
      name: "Human-readable label for this check"   # optional
      with: { input: output_name, operator: not_null }
  if: "${{ success() }}"
  timeout_s: 30.0

Field order: id → uses → name → with → returns → validate → if → timeout_s


Variable References (${{ ... }})

Pattern Resolves to
${{ env.provider_url }} TCK environment variable
${{ env.services.name.output }} Service output (e.g., .connector_service)
${{ env.testdata.data_name }} Testdata file content
${{ env.schemas.schema_name }} Schema file content
${{ steps.step_id.output_name }} Output from a step in steps:
${{ setup.step_id.output_name }} Output from a step in setup:
${{ metadata.dataspace_version }} Metadata field value
${{ execution.id }} The id of this run (the job id), e.g. to name what a test leaves in a shared system

Conditionals (if:)

Expression Meaning
${{ success() }} All previous steps passed (default)
${{ failure() }} At least one previous step failed
${{ always() }} Always executes
${{ steps.step_id.outcome == 'success' }} Specific step outcome check
${{ steps.step_id.outcome == 'failure' }} Specific step failed
${{ steps.step_id.outcome == 'skipped' }} Specific step was skipped

Returns Block

YAML
returns:
  output_name:
    type: string          # string | integer | object | array | boolean
    class: AssetId        # Semantic class (for type filtering)

Common classes: AssetId, PolicyId, AgreementId, AuthToken, StatusCode, ResponseBody, ResponseHeaders, Uuid, Url, Bpn, ConnectorService, DataplaneUrl, MockInstance, Policy, String


Validate Block

Assertions reference the step's own output names directly (not ${{ }} for local outputs):

YAML
validate:
  - uses: validate/assert
    name: "the request is accepted"   # optional; the report calls the check this
    with:
      input: status_code
      operator: equals
      value: 200
  - uses: validate/assert
    with:
      input: "${{ steps.other_step.output }}"  # cross-step refs use ${{ }}
      operator: not_null

Operators:

Operator Description
equals Exact match
not_equals Not equal
not_null Value is not null
not_empty Value is not empty
contains Contains substring/element
not_contains Does not contain
regex Matches regular expression
status_code HTTP status code match
greater_than Numeric >
less_than Numeric <
greater_or_equal Numeric ≥
less_or_equal Numeric ≤
between Numeric range (inclusive)

JSON Schema Validation (validate/schema)

validate/schema validates a value against a full JSON Schema document — distinct from the scalar operators above, which compare single values. The schema is normally a reference to a file declared in the TCK env.schemas block; the step fails (marking the step FAILED) when the payload does not conform, reporting the offending field paths.

YAML
- id: validate_twin
  uses: validate/schema
  with:
    input: "${{ steps.query_dt.response_body }}"     # dict, list, or JSON string
    schema: "${{ env.schemas.shell_descriptor_schema }}"

An inline schema object is also accepted — useful for a one-off existence check, e.g. asserting an array contains an element with a given field:

YAML
- id: has_submodel_value_endpoint
  uses: validate/schema
  with:
    input: "${{ steps.query_dt.response_body }}"
    schema:
      type: object
      required: [submodelDescriptors]
      properties:
        submodelDescriptors:
          type: array
          contains:
            type: object
            properties:
              endpoints:
                type: array
                contains:
                  properties:
                    interface: { const: SUBMODEL-VALUE-3.1 }

Extracting Values (json_path_extract)

json_path_extract reads a value out of a dict/list using a dot-separated path, storing it in a variable for later steps.

YAML
- id: get_asset_id
  uses: json_path_extract
  with:
    source: response_body        # variable NAME, or a ${{ }} expression
    path: "datasets.0.id"        # dot path; numeric segments index lists
    store_in_variable: asset_id  # optional; step output is the value either way
  • source — either the name of a context variable (response_body), or a ${{ }} expression that resolves to the data itself ("${{ steps.query.response_body }}"). Both forms work.
  • path — dot-separated. Numeric segments index into lists (endpoints.0.href).

Predicate filters — select the first list element whose field matches a value, instead of relying on a positional index:

Path Selects
items[id=abc].value first items element with id == abc
endpoints[interface='SUBMODEL-VALUE-3.1'] quote values that contain ., ;, #, etc.
descriptors.endpoints[interface='…'] steps over an intermediate array — no index needed
descriptors[endpoints.interface='…'].id select by a nested field (dotted predicate key)

Quote a predicate value ('…') when it contains dots or other separators — interface names and semantic IDs always need quoting. A predicate that matches nothing raises a clear error rather than returning a wrong value.


Utility Steps

util/parse_kv — parse a delimited key=value string (e.g. an EDC subprotocolBody) into a dict, or select one key. Each pair is split on the first = only, so a value may itself contain = (a URL query string, base64).

YAML
- id: get_edc_asset_id
  uses: util/parse_kv
  with:
    input: "${{ subprotocol_body }}"   # "dspEndpoint=https://…;id=urn:uuid:1234"
    select: id                          # omit to return the whole dict
    store_in_variable: edc_asset_id
    # pair_separator: ";"   (default)
    # kv_separator: "="     (default)

util/base64 — encode or decode a string with base64 / base64url. The AAS DTR requires an aas_identifier to be base64url-encoded before it goes in a request path, so url_safe: true is the common case. Decoding restores padding automatically, so an unpadded value round-trips without extra =.

YAML
- id: encode_aas_id
  uses: util/base64
  with:
    input: "${{ env.twin_id }}"   # "urn:uuid:1234" or a URL
    mode: encode                             # encode (default) | decode
    url_safe: true                           # -/_ instead of +//; needed for DTR
    strip_padding: false                     # drop trailing '=' when encoding
    store_in_variable: aas_identifier_b64

util/log — echo a resolved value to stdout and the run log while authoring. Asserts nothing and always passes; remove once a test is finalised.

YAML
- id: show_href
  uses: util/log
  with:
    message: SUBMODEL-VALUE-3.1 href
    value: "${{ submodel_value_href }}"

Complex Variables (TCK only)

Typed variables in env.variables cover everything a test needs before its steps run: runtime inputs and reusable config objects such as policies and assets. Declare each one once and reference it by its id — ${{ env.<id> }}. Every variable publishes one value under value, whatever its type, and its uses: verb decides that type: the compiler refuses a returns: that says otherwise.

YAML
env:
  variables:
    # Runtime input collected from the SUT operator
    - id: sut_dsp_url
      uses: variable/type/string
      name: SUT DSP Endpoint URL
      with:
        source: input
        scope: sut
        placeholder: "https://connector.example.com/api/dsp"
      returns:
        value:
          type: string

    # Constant value with a default
    - id: sut_response_timeout
      uses: variable/type/integer
      with:
        value: 300
      returns:
        value:
          type: integer

    # Reusable access policy, referenced wherever a step needs it
    - id: usage_policy
      uses: config/connector/policy
      name: Required usage policy
      with:
        value:
          permissions:
            - action: use
              constraints:
                and:
                  - left_operand: UsagePurpose
                    operator: isAnyOf
                    right_operand: "cx.ccm.base:1"
      returns:
        value:
          type: object
          class: Policy

    # Asset definition the provider steps provision
    - id: api_asset
      uses: config/connector/asset
      name: API asset
      with:
        value:
          name: CCMAPI Notification Asset
          base_url: "https://backend.example.com/ccm"
          properties:
            dct:type:
              "@id": "https://w3id.org/catenax/taxonomy#CCMAPI"
            cx-common:version: "3.0"
      returns:
        value:
          type: object
          class: Asset

Reference in tests: ${{ env.sut_dsp_url }}, ${{ env.usage_policy }}, ${{ env.api_asset }} — the id alone, for every type.


Failure Handling

There is no per-step failure policy. A failed validation (hard assertion) fails its step; a failed step fails the test — execution stops, the remaining steps are skipped, and teardown runs. Teardown steps keep executing even when one of them fails.

Common uses: Handlers

Prefix Examples
mock/ mock/api, mock/wait/http_request
connector/ connector/pull_data_filtered, connector/create_asset, connector/health_check
util/ util/generate_uuid, util/log, util/parse_kv, util/base64
validate/ validate/assert, validate/field, validate/schema
variable/ variable/type/string, variable/type/integer, variable/type/boolean
config/ config/connector/policy, config/connector/asset
service/ service/connector_service
(no prefix) json_path_extract

See Extracting Values and Utility Steps below for the extraction and helper handlers.

Dataspace Versions

Version EDC Compatibility Protocol
saturn EDC v0.11+ DSP 2025-1
jupiter EDC v0.8–0.10 Legacy DSP

Set in metadata.dataspace_version or per-service via with.dataspace_version.

Variable Resolution Priority

  1. Runtime variables — CLI --var flags or API input
  2. TCK environment — env.variables in the TCK file
  3. Step outputs — resolved at execution time