# Step Reference

> Complete reference of every KTestify Gherkin step with all DataTable columns, types, defaults, and examples.

Source: https://docs.ktestify.xyz/docs/write-tests/step-reference

Complete catalogue of every KTestify step. All DataTable columns are listed with their type, whether they are required, the default value (if any), and an inline example.

---

## Background: `Given` steps

### `Given namespace`

Declares a single namespace prefix applied to all topic names.

```gherkin
Given namespace
  | namespace |
  | myapp     |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `namespace` | `string` | ✅ | none | Namespace prefix (e.g. `myapp`, `com.example`) |

---

### `Given namespaces`

Declares multiple named namespace prefixes.

```gherkin
Given namespaces
  | namespace   | namespaceAlias |
  | myapp.input | input-ns       |
  | myapp.audit | audit-ns       |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `namespace` | `string` | ✅ | none | Namespace prefix |
| `namespaceAlias` | `string` | ✅ | none | Alias referenced in topic declarations |

---

### `Given input topic`

Declares one or more Kafka topics as **producers** (INPUT type).

```gherkin
Given input topic
  | topicName  | topicAlias | namespace | namespaceAlias |
  | orders-in  | orders-in  |           |                |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicName` | `string` | ✅ | none | Physical topic name (namespace prepended) |
| `topicAlias` | `string` | ✅ | none | Alias for use in `When` steps |
| `namespace` | `string` | ❌ | Config default | Override namespace for this topic |
| `namespaceAlias` | `string` | ❌ | none | Reference a `Given namespaces` alias |

---

### `Given output topic`

Declares one or more Kafka topics as **consumers** (OUTPUT type).

```gherkin
Given output topic
  | topicName   | topicAlias | namespace | namespaceAlias |
  | orders-out  | enriched   | myapp     |                |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicName` | `string` | ✅ | none | Physical topic name (namespace prepended) |
| `topicAlias` | `string` | ✅ | none | Alias for use in `Then` steps |
| `namespace` | `string` | ❌ | Config default | Override namespace for this topic |
| `namespaceAlias` | `string` | ❌ | none | Reference a `Given namespaces` alias |

---

### `Given assets directory`

Sets the base directory for all relative file paths in the scenario.

```gherkin
Given assets directory
  | absolutePath              |
  | src/test/resources/assets |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `absolutePath` | `string` | ✅ | `KTESTIFY_ASSETS_DIR` / config | Absolute or project-relative path |

---

### `Given schema`

Declares an Avro schema for use in Avro producer/consumer steps.

```gherkin
Given schema
  | schemaName | schemaAlias | schemaVersion |
  | Order      | order       | 1             |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `schemaName` | `string` | ✅ | none | Schema Registry subject name (prefix for `schemaName-value`) |
| `schemaAlias` | `string` | ✅ | none | Short alias |
| `schemaVersion` | `integer` | ❌ | latest | Schema version to use |

---

## Actions: `When` steps

:::info[Multi-row DataTables]
Producer and script steps below accept multiple DataTable rows, executed sequentially in order. Producer steps additionally require every row to resolve to the **same physical topic** (`TopicMismatchException` otherwise). See [Multi-row DataTables](advanced/multi-row-datatables) for full details.
:::

### `When record from file is sent`

Produces a raw String message to a Kafka input topic.

```gherkin
When record from file is sent
  | topicName | file         | recordKey | headerFile        |
  | orders-in | payload.json | key-001   | headers.json      |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicName` | `string` | ✅ | none | Topic name or alias (must be declared as INPUT) |
| `file` | `string` | ✅ | none | Payload file, relative to assets directory |
| `recordKey` | `string` | ❌ | `null` | Kafka record key |
| `headerFile` | `string` | ❌ | none | JSON file of Kafka headers `{"name":"value"}` |

---

### `When record from file based on schema is sent`

Produces an Avro-serialised message.

```gherkin
When record from file based on schema is sent
  | topicName | file       | schemaName | recordKey |
  | orders-in | order.json | Order      | ord-001   |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicName` | `string` | ✅ | none | Input topic name or alias |
| `file` | `string` | ✅ | none | JSON payload file |
| `schemaName` | `string` | ❌ | TopicNameStrategy | Schema alias or name declared via `Given schema` |
| `recordKey` | `string` | ❌ | `null` | Kafka record key |

---

### `And wait for {int} seconds`

Pauses execution.

```gherkin
And wait for 5 seconds
```

| Parameter | Type | Required | Description |
|---|---|---|---|
| `{int}` | `integer` | ✅ | Seconds to pause |

---

### `When script is executed` / `And execute script`

Runs a shell script.

```gherkin
When script is executed
  | scriptPath          | scriptArgs     |
  | ./scripts/reset.sh  | arg1,arg2      |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `scriptPath` | `string` | ✅ | none | Path to the script file |
| `scriptArgs` | `string` | ❌ | none | Comma-separated arguments |

Fails if exit code ≠ 0.

---

## Assertions: `Then` / `And` steps

:::info[Multi-row DataTables]
Every single-record assertion step below (all except the two batch steps) accepts multiple DataTable rows, validated sequentially in order, **provided every row targets the same physical topic** (`TopicMismatchException` otherwise). All rows in the same step share one pinned `referenceTimestamp` so delta-time seek windows don't drift row-to-row. The batch steps (`expected records from files` / `...based on schema`) remain strictly single-row. See [Multi-row DataTables →](advanced/multi-row-datatables) for full details.
:::

### `Then expected record from file`

Raw file match, full value equality.

```gherkin
Then expected record from file
  | topicAlias | file          | expectedRecordKey | keyMatchStrategy | consumerReadTimeout | consumerDeltaTime |
  | orders-out | expected.json | key-001           | exact            | 30                  | 60                |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicAlias` | `string` | ✅ | none | Output topic alias |
| `file` | `string` | ✅ | none | Expected file path |
| `expectedRecordKey` | `string` | ❌ | none | Filter by record key |
| `keyMatchStrategy` | `string` | ❌ | `exact` | Key matching strategy: `exact`, `contains`, `starts_with`, `ends_with`, `regex` |
| `consumerReadTimeout` | `integer` | ❌ | Config (30) | Seconds to wait |
| `consumerDeltaTime` | `integer` | ❌ | Config (60) | Seconds to seek back |

---

### `Then expected record should have fields matching from file`

Positional character field match.

```gherkin
Then expected record should have fields matching from file
  | topicAlias | file         | line | from | to |
  | orders-out | expected.txt | 2    | 0    | 15 |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicAlias` | `string` | ✅ | none | Output topic alias |
| `file` | `string` | ✅ | none | Expected file |
| `line` | `integer` | ✅ | none | 1-based line number |
| `from` | `integer` | ✅ | none | Start char index (inclusive) |
| `to` | `integer` | ✅ | none | End char index (exclusive) |

---

### `Then expected record from file based on XML`

Structural XML comparison with optional element exclusions.

```gherkin
Then expected record from file based on XML
  | topicAlias | file         | excludedElements    | expectedRecordKey | keyMatchStrategy |
  | orders-out | expected.xml | ns:CreationDateTime |                   |                  |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicAlias` | `string` | ✅ | none | Output topic alias |
| `file` | `string` | ✅ | none | Expected XML file |
| `excludedElements` | `string` | ❌ | none | Comma-separated element names to skip |
| `expectedRecordKey` | `string` | ❌ | none | Filter by record key |
| `keyMatchStrategy` | `string` | ❌ | `exact` | Key matching strategy: `exact`, `contains`, `starts_with`, `ends_with`, `regex` |
| `consumerReadTimeout` | `integer` | ❌ | Config (30) | Seconds to wait |
| `consumerDeltaTime` | `integer` | ❌ | Config (60) | Seconds to seek back |

---

### `Then expected record based on XML should have fields matching from file`

XPath extraction + comparison.

```gherkin
Then expected record based on XML should have fields matching from file
  | topicAlias | file         | xpathExpressions                              |
  | orders-out | expected.txt | //Weight[@unit='KG']/text(),//Sender/ID/text() |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicAlias` | `string` | ✅ | none | Output topic alias |
| `file` | `string` | ✅ | none | Expected values file (one per line) |
| `xpathExpressions` | `string` | ✅ | none | Comma-separated XPath expressions |

---

### `Then expected record from file based on schema`

Avro → JSON file match with optional field exclusions.

```gherkin
Then expected record from file based on schema
  | topicAlias | file          | excludedKeys      | expectedRecordKey | keyMatchStrategy | consumerReadTimeout |
  | orders-out | expected.json | createdAt,eventId | ord-001           | exact            | 30                  |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicAlias` | `string` | ✅ | none | Output topic alias |
| `file` | `string` | ✅ | none | Expected JSON file |
| `excludedKeys` | `string` | ❌ | none | Comma-separated Avro field names to exclude |
| `expectedRecordKey` | `string` | ❌ | none | Filter by record key |
| `keyMatchStrategy` | `string` | ❌ | `exact` | Key matching strategy: `exact`, `contains`, `starts_with`, `ends_with`, `regex` |
| `consumerReadTimeout` | `integer` | ❌ | Config (30) | Seconds to wait |
| `consumerDeltaTime` | `integer` | ❌ | Config (60) | Seconds to seek back |

---

### `Then expected record based on schema should have fields matching from given value`

Assert specific Avro field values without a file.

**Single field** (use `key` / `value` columns):

```gherkin
Then expected record based on schema should have fields matching from given value
  | topicAlias | key    | value     |
  | orders-out | status | PROCESSED |
```

**Multiple fields** (use `keys` / `values` columns, semicolon-separated):

```gherkin
Then expected record based on schema should have fields matching from given value
  | topicAlias | keys          | values      |
  | orders-out | status;amount | PENDING;99 |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicAlias` | `string` | ✅ | none | Output topic alias |
| `key` | `string` | ❌* | none | Avro field name (single-field mode) |
| `value` | `string` | ❌* | none | Expected field value (single-field mode) |
| `keys` | `string` | ❌* | none | Semicolon-separated Avro field names (multi-field mode) |
| `values` | `string` | ❌* | none | Semicolon-separated expected values (multi-field mode) |
| `consumerReadTimeout` | `integer` | ❌ | Config (30) | Seconds to wait |
| `consumerDeltaTime` | `integer` | ❌ | Config (60) | Seconds to seek back |

\* Either `key` + `value` or `keys` + `values` must be provided.

---

### `Then expected records from files`

Raw batch, collect N records and match against N expected files (positionally by default, or in any order when `batchMatchStrategy` is `lazy`).

```gherkin
Then expected records from files
  | topicAlias | expectedRecordsCount | files                              | batchMatchStrategy | consumerReadTimeout |
  | orders-out | 3                    | f1.json,f2.json,f3.json            | positional         | 60                  |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicAlias` | `string` | ✅ | none | Output topic alias |
| `expectedRecordsCount` | `integer` | ✅ | none | Exact number of records to collect |
| `files` | `string` | ✅ | none | Comma-separated expected file paths |
| `batchMatchStrategy` | `string` | ❌ | `positional` | Batch matching strategy (`positional`, `lazy`) |
| `expectedRecordKeys` | `string` | ❌ | none | Comma-separated expected keys paired 1-to-1 with `files` (or a single key shared across all records in the batch), filtered during polling and asserted via `batchMatchStrategy` and `keyMatchStrategy` |
| `expectedRecordKey` | `string` | ❌ | none | Single key filter applied while collecting batch records |
| `keyMatchStrategy` | `string` | ❌ | `exact` | Key matching strategy (`exact`, `contains`, `starts_with`, `ends_with`, `regex`) |
| `consumerReadTimeout` | `integer` | ❌ | Config (30) | Seconds to wait for all records |
| `consumerDeltaTime` | `integer` | ❌ | Config (60) | Seconds to seek back |

---

### `Then expected records from files based on schema`

Avro batch, collect N Avro records and match against N expected files (positionally by default, or in any order when `batchMatchStrategy` is `lazy`).

```gherkin
Then expected records from files based on schema
  | topicAlias | expectedRecordsCount | files                 | excludedKeys | batchMatchStrategy | consumerReadTimeout |
  | orders-out | 2                    | exp-1.json,exp-2.json | ts,eventId   | lazy               | 60                  |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicAlias` | `string` | ✅ | none | Output topic alias |
| `expectedRecordsCount` | `integer` | ✅ | none | Exact number of records to collect |
| `files` | `string` | ✅ | none | Comma-separated expected JSON file paths |
| `excludedKeys` | `string` | ❌ | none | Avro field names to exclude from all records |
| `batchMatchStrategy` | `string` | ❌ | `positional` | Batch matching strategy (`positional`, `lazy`) |
| `expectedRecordKeys` | `string` | ❌ | none | Comma-separated expected keys paired 1-to-1 with `files` (or a single key shared across all records in the batch), filtered during polling and asserted via `batchMatchStrategy` and `keyMatchStrategy` |
| `expectedRecordKey` | `string` | ❌ | none | Single key filter applied while collecting batch records |
| `keyMatchStrategy` | `string` | ❌ | `exact` | Key matching strategy (`exact`, `contains`, `starts_with`, `ends_with`, `regex`) |
| `consumerReadTimeout` | `integer` | ❌ | Config (30) | Seconds to wait for all records |
| `consumerDeltaTime` | `integer` | ❌ | Config (60) | Seconds to seek back |

---

### `And record should appear in topic`

Positive watcher, asserts at least one record is present.

```gherkin
And record should appear in topic
  | topicAlias | topicType | expectedRecordKey | keyMatchStrategy | consumerReadTimeout | consumerDeltaTime |
  | orders-out | raw       | order-            | starts_with      | 15                  | 60                |
```

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicAlias` | `string` | ✅ | none | Output topic alias |
| `topicType` | `string` | ✅ | none | `raw` or `avro` |
| `expectedRecordKey` | `string` | ❌ | none | Filter by record key |
| `keyMatchStrategy` | `string` | ❌ | `exact` | Key matching strategy: `exact`, `contains`, `starts_with`, `ends_with`, `regex` |
| `consumerReadTimeout` | `integer` | ❌ | Config (30) | Seconds to wait |
| `consumerDeltaTime` | `integer` | ❌ | Config (60) | Seconds to seek back |

---

### `And record should not appear in topic`

Negative watcher, asserts no record appears within the timeout.

```gherkin
And record should not appear in topic
  | topicAlias | topicType | expectedRecordKey | keyMatchStrategy | consumerReadTimeout | consumerDeltaTime |
  | orders-out | raw       | non-existent      | exact            | 10                  | 30                |
```

Same columns as `record should appear in topic`.

---

## Matcher × Topic type compatibility

| Matcher step | `raw` topics | `avro` topics |
|---|---|---|
| `expected record from file` | ✅ | ❌ use `based on schema` |
| `expected record from file based on XML` | ✅ | ❌ not available |
| `expected record based on XML / XPath` | ✅ | ❌ not available |
| `expected record from file based on schema` | ❌ | ✅ |
| `expected record based on schema / field value` | ❌ | ✅ |
| `expected records from files` (batch) | ✅ | ❌ |
| `expected records from files based on schema` (batch) | ❌ | ✅ |
| `record should (not) appear in topic` | ✅ both | ✅ both |

---

## Key matching strategies

When a step includes an `expectedRecordKey` column, you can optionally specify a `keyMatchStrategy` column to control how the expected key is matched against the actual record key. This is useful when record keys are dynamically generated (UUIDs, prefixed keys, timestamps) and an exact match is too restrictive.

| Strategy | DataTable value | Behaviour | Example |
|---|---|---|---|
| `EXACT` | `exact` | Exact string equality (default) | `"order-001"` matches `"order-001"` |
| `CONTAINS` | `contains` | Expected key is a substring of the actual key | `"order"` matches `"order-001"` |
| `STARTS_WITH` | `starts_with` | Actual key starts with the expected key | `"order-"` matches `"order-001"` |
| `ENDS_WITH` | `ends_with` | Actual key ends with the expected key | `"001"` matches `"order-001"` |
| `REGEX` | `regex` | Actual key matches the expected Java regex pattern | `"order-\\d+"` matches `"order-001"` |

:::tip[Parsing]
The `keyMatchStrategy` value is case-insensitive and tolerant of hyphens, underscores, and spaces. For example, `starts_with`, `starts-with`, `starts_with`, `StartsWith`, and `starts with` all resolve to `STARTS_WITH`. Unrecognised values default to `EXACT`.
:::

:::warning[Topic isolation]
When using non-exact strategies (`contains`, `regex`, `starts_with`), ensure your match pattern is specific enough to uniquely identify the intended record. Kafka topics may contain records from prior scenarios in the same feature file, and a broad pattern (e.g. `contains "key"`) may match a decoy record first, causing value mismatches.
:::

### Example: matching a UUID-prefixed key

```gherkin
# Record key is "550e8400-e29b-41d4-a716-446655440000"
Then expected record from file
  | topicAlias | file          | expectedRecordKey | keyMatchStrategy | consumerReadTimeout |
  | orders-out | expected.json | 550e8400          | starts_with      | 30                  |
```

### Example: matching a dynamic key with regex

```gherkin
# Record key is "ORD-2026-000001"
Then expected record from file
  | topicAlias | file          | expectedRecordKey | keyMatchStrategy | consumerReadTimeout |
  | orders-out | expected.json | ORD-\d{4}-\d{6}   | regex            | 30                  |
```

---

## Batch matching strategies

In batch assertion steps (`Then expected records from files` and `Then expected records from files based on schema`), the optional `batchMatchStrategy` column controls how collected records are compared against the comma-separated list of expected `files`:

| Strategy | Accepted DataTable values | Behaviour |
|---|---|---|
| `POSITIONAL` | `positional`, `ordered`, `index`, `strict` | Default. Record at index `i` must match file at index `i` (and `expectedRecordKeys[i]` when set). |
| `LAZY` | `lazy`, `fuzzy`, `unordered`, `any_order`, `any-order` | Order-independent 1-to-1 matching without replacement. Each consumed record claims the first unmatched expected file (and corresponding `expectedRecordKeys[j]` when set) whose content and key match. |

:::tip[Parsing]
The `batchMatchStrategy` value is case-insensitive and normalizes hyphens and spaces to underscores. Unrecognised or blank values default to `POSITIONAL`.
:::

```gherkin
Then expected records from files
  | topicAlias | expectedRecordsCount | files                              | batchMatchStrategy | consumerReadTimeout |
  | orders-out | 3                    | f1.json,f2.json,f3.json            | lazy               | 30                  |
```

---

## See also

- [Multi-row DataTables →](advanced/multi-row-datatables)
- [Timeout tuning →](advanced/timeout-tuning)
- [Batch testing →](advanced/batch-testing)
