# Watcher: `And record should (not) appear in topic`

> Assert that a record appears or does not appear on an output topic within the timeout window.

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

The watcher steps don't compare record content, they simply verify that **at least one record** (optionally matching a key) does or does not appear on a topic within the read timeout window.

---

## Record should appear, positive assertion

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

Fails if **no** record appears within `consumerReadTimeout` seconds.

---

## Record should not appear, negative assertion

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

Passes only if **no** record appears within `consumerReadTimeout` seconds. Use this to assert that a message is **filtered out** or **not produced** by your pipeline.

---

## Column reference

| Column | Type | Required | Description |
|---|---|---|---|
| `topicAlias` | `string` | ✅ | Alias of a declared output topic |
| `topicType` | `string` | ✅ | `raw` or `avro`, determines the deserialiser |
| `consumerReadTimeout` | `integer` | ❌ | Max seconds to wait (or confirm absence) |
| `consumerDeltaTime` | `integer` | ❌ | How far back (seconds) to seek |

:::tip[Choosing the right timeout for negative assertions]
For `should not appear`, set `consumerReadTimeout` to a value that is long enough to be confident nothing will arrive, but short enough to not slow your suite down unnecessarily. A value between 5 and 10 seconds is usually a good starting point.
:::

---

## Multiple rows, same topic

Both watcher steps support multiple rows, which is useful for checking presence or absence of several distinct keys on the same topic in one step. Rows are validated sequentially, in order, and share one pinned `referenceTimestamp` so their seek windows don't drift. Every row must target the **same physical topic**. Mixing topics throws `TopicMismatchException` before any consumer call is made:

```gherkin
And record should not appear in topic
  | topicAlias | topicType | expectedRecordKey | consumerReadTimeout |
  | orders-out | raw       | order-invalid-1   | 10                  |
  | orders-out | raw       | order-invalid-2   | 10                  |
```

See [Multi-row DataTables](../advanced/multi-row-datatables) for the full rules.

---

## Example: filtering scenario

```gherkin
Scenario: Invalid orders are filtered out
  When record from file is sent
    | topicName | file              | recordKey   |
    | orders-in | order-invalid.json | order-invalid |
  And record should not appear in topic
    | topicAlias | topicType | consumerReadTimeout |
    | orders-out | raw       | 10                  |
```
