How Drift Verifies Message-Based APIs
Drift verifies that a message-based API implementation matches the AsyncAPI document that describes it. This page explains how that verification works, what Drift needs from you, and where the boundaries of the feature lie.
If you want to write a test now, start with AsyncAPI.
How Message-Based Verification Works
For an HTTP API, a single operation is self-contained. Drift reads the operation from your OpenAPI document, sends a request, and receives a response it can validate immediately. The whole interaction happens inside one function call.
Message-based APIs do not work this way. A service publishes an event some time after something else happened. A service consumes a command and the only evidence is a change in state somewhere else. A request and its reply travel over separate channels and are matched by a correlation identifier.
The contract is still valuable, but it does not contain enough information for Drift to drive and observe the whole interaction on its own. Drift needs your help at two points: to start the behavior, and sometimes to observe the result.
Components of an AsyncAPI Test
lowchart LR
subgraph YOURS["You provide"]
SPEC["AsyncAPI 3.x<br/>document"]
TC["Drift test case<br/>YAML"]
HOOK["Trigger and probe<br/>hooks"]
BRK[("Running broker<br/>Kafka / SNS / SQS")]
end
subgraph DRIFT["Drift"]
AP["asyncapi plugin<br/><i>plans the interaction</i>"]
TP["kafka / aws-messaging<br/><i>talks to the broker</i>"]
JP["json plugin<br/><i>validates payloads</i>"]
end
subgraph OUT["Results"]
CON["Console output"]
JU["JUnit XML"]
PF["PactFlow<br/>verification bundle"]
end
SPEC --> AP
TC --> AP
AP --> TP
AP --> JP
HOOK -.->|"Drift runs your hooks<br/>at the right moment"| TP
TP <-->|"real protocol traffic"| BRK
JP --> CON
JP --> JU
JP --> PF
AP --> HOOK
style YOURS fill:#f6f8fa,stroke:#8b96a3,color:#111
style DRIFT fill:#e8f4f8,stroke:#2b7a9b,color:#0b2b36
style OUT fill:#f2f7ee,stroke:#6b8f4e,color:#1e2b12An AsyncAPI 3.x document. Drift reads the channel address, the message schema, the headers schema, the correlation ID location, and the server connection details from your contract.
A Drift test case. This selects the operation to verify, supplies the payload and headers where Drift is publishing, and states what you expect.
A running broker. Drift does not provide one. See the next section.
Trigger and probe hooks. Small commands or Lua functions that start behavior in your service and observe its side effects. See Using Triggers and Probes.
Broker and Transport Requirements
Drift connects to your broker and exchanges real messages over the real protocol. It publishes to real topics and queues, and it subscribes to them to capture what your service produces.
This is a deliberate design choice, and it distinguishes Drift from mock-based approaches. Verifying against a real transport means your broker configuration, your serialization, your headers, and your correlation behavior are all exercised. A test that passes tells you the implementation works over the wire, not only that a message-shaped object matched a schema.
The trade-off is that you must provide the transport. Drift does not ship a mock or in-memory broker, and it does not start a broker for you.
Use isolated or disposable environments. A local broker started with Docker Compose or Testcontainers, or a local cloud emulator such as LocalStack, is the recommended setup. It is fast, it is repeatable, and nothing you publish affects anyone else.
You can run Drift against a shared environment, but that environment is yours to manage. Messages Drift publishes are real messages, and downstream consumers will process them. Drift does not guarantee side-effect-free execution in a shared system.
Execution Modes
Drift verifies one declared interaction at a time. It selects an execution mode based on the AsyncAPI operation and your test case, then runs the sequence for that mode. The four modes are covered in full in AsyncAPI Execution Modes. The two shapes below are the ones worth understanding conceptually.
Observing a Message Your Service Publishes
Your service emits an event. You want to confirm the event matches the contract.
Drift subscribes to the channel first, then runs your trigger hook, then waits for a matching message.
Subscribing before triggering matters: if Drift subscribed afterwards, a fast service could publish before Drift was listening, and the message would be lost.
Diagram
autonumber
participant D as Drift
participant B as Broker
participant S as Your service
D->>D: Read the AsyncAPI operation<br/>and plan the interaction
D->>B: Subscribe to the channel
Note over D,B: Drift subscribes first, so no<br/>message can be missed
D->>S: Run the trigger hook
Note right of S: Your command or Lua function<br/>causes the service to publish
S->>B: Publish message
B-->>D: Deliver matching message
Note over D: Match on correlation ID,<br/>within the capture window
D->>D: Validate payload and headers<br/>against the AsyncAPI schemaInjecting a Message Your Service Consumes
Your service consumes a command. There is no response to validate, so you need to tell Drift how to check that the command was handled.
Drift publishes the message, then runs your probe hook, and compares what the probe returns against your expectations.
sequenceDiagram
autonumber
participant D as Drift
participant B as Broker
participant S as Your service
participant E as Side effect<br/>(database, API, file)
D->>D: Read the AsyncAPI operation<br/>and build the message
D->>B: Publish the message
B-->>S: Deliver the message
S->>E: Process and record the result
D->>D: Run the probe hook
D->>E: Probe queries the side effect
E-->>D: Probe returns JSON evidence
D->>D: Compare the probe output<br/>against your expectationsReading the AsyncAPI Action Correctly
AsyncAPI 3 describes operations from the point of view of the application the document describes - your service. This is the most common source of confusion when writing your first test case.
AsyncAPI action | What your service does | What Drift does |
|---|---|---|
| Your service publishes the message | Drift subscribes and captures it |
| Your service consumes the message | Drift publishes it |
Drift's role is always the opposite of your service's role.
Correlation and Capture Windows
When Drift subscribes to a channel, other traffic may be present. Drift needs to know which message is the one it is waiting for.
Correlation. Drift matches on a correlation identifier. Your AsyncAPI document declares where that identifier lives, through the message's correlationId location - most commonly a header. Declaring the location is what enables correlation, and it is the part you must supply.
The value is optional. Drift takes it from your AsyncAPI document if one is set there, then from the correlation-id parameter in your test case, and generates a UUID when neither supplies one. Set the value yourself when a hook, a probe, or an expectation needs to reference the same identifier; let Drift generate it when nothing outside the operation needs to know it.
Capture window. Drift waits for a bounded period, set with timeout-ms. If no matching message arrives before the window closes, the operation fails with a capture timeout rather than hanging.
Choosing a window is a trade-off. Too short and a slow service produces flaky failures. Too long and a genuinely broken service takes a long time to report. Start at a few seconds for a local broker and tune from there.
What Drift Does Not Verify
Drift verifies contract conformance for a single declared interaction. The following are outside its scope and belong to functional or end-to-end testing:
Multi-step choreography. Drift does not run sagas or verify that a sequence of messages occurs in order across several services.
Message ordering. Drift captures the first message matching your correlation ID. It does not assert the relative order of a stream.
Delivery guarantees. Drift does not verify at-least-once or exactly-once semantics, retry behavior, or dead-letter handling.
Throughput and performance. Drift is not a load-testing tool.
Consumer behavior beyond the probe. Drift knows only what your probe hook tells it.
This narrow scope is what makes Drift fast and deterministic. For the wider picture of how Drift complements other test types, see Where Drift Fits.