# Raw Record Assertions

> Assert raw (String/JSON/text) Kafka records against files or positional fields.

Source: https://docs.ktestify.xyz/docs/write-tests/assertions/raw-matchers

Raw matchers compare String-serialised Kafka record values against an expected file or positional field range. These steps apply to topics declared as raw (non-Avro).

---

## File match: `Then expected record from file`

Fetches the next record from the output topic and asserts its value matches the content of an expected file character-for-character (with coloured diff on failure).

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

| Column | Type | Required | Default | Description |
|---|---|---|---|---|
| `topicAlias` | `string` | ✅ | none | Alias of a declared output topic |
| `file` | `string` | ✅ | none | Expected file path, relative to assets directory |
| `expectedRecordKey` | `string` | ❌ | none | Filter: only accept records with this exact key |
| `consumerReadTimeout` | `integer` | ❌ | From config (30s) | Max seconds to wait for a record |
| `consumerDeltaTime` | `integer` | ❌ | From config (60s) | How far back (seconds) to seek before consuming |

---

## Key + value match: `Then expected record with key and value from file`

Asserts both the record **key** and **value** match the content of an expected file.

```gherkin
Then expected record with key and value from file
  | topicAlias | file            | consumerReadTimeout |
  | orders-out | expected.json   | 30                  |
```

The expected file must contain the key on the first line and the value starting from the second line (same format as `FileKeyRecordMatcher`).

---

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

Asserts a specific character range within a specific line of the record value.

```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 | Description |
|---|---|---|---|
| `topicAlias` | `string` | ✅ | Alias of a declared output topic |
| `file` | `string` | ✅ | Expected file path |
| `line` | `integer` | ✅ | 1-based line number in the record value |
| `from` | `integer` | ✅ | Start character index (inclusive) |
| `to` | `integer` | ✅ | End character index (exclusive) |

---

## Record key match: `Then expected record should match key`

Asserts only the **record key** (value is ignored).

```gherkin
Then expected record should match key
  | topicAlias | expectedRecordKey | consumerReadTimeout |
  | orders-out | order-001         | 30                  |
```

---

## Multiple rows, same topic

Add multiple rows to validate several records in one step, sequentially, in order, as long as every row targets the **same physical topic**:

```gherkin
Then expected record from file
  | topicAlias | file             | expectedRecordKey | consumerReadTimeout | consumerDeltaTime |
  | orders-out | expected-1.json  | order-001          | 30                   | 60                |
  | orders-out | expected-2.json  | order-002          | 30                   | 60                |
```

All rows in the same step share one pinned `referenceTimestamp` ("now" captured once at the top of the step), so their `consumerDeltaTime` seek windows are computed identically instead of drifting row-to-row. Mixing topics in one DataTable throws `TopicMismatchException` before any consumer call is made. See [Multi-row DataTables](../advanced/multi-row-datatables) for the full rules, including why batch steps stay single-row.

---

## See also

- [XML matchers →](xml-matchers)
- [Avro matchers →](avro-matchers)
- [Batch assertions →](batch-assertions)
- [Watcher →](watcher)
- [Timeout tuning →](../advanced/timeout-tuning)
- [Multi-row DataTables →](../advanced/multi-row-datatables)
