Non-Deterministic Behavior
Message delivery order isn't guaranteed across partitions. Tests that pass locally may fail in CI due to race conditions.
Deterministic Kafka consumer tests with @EmbeddedKafka, partition-ordering guarantees, and failure injection — the exact pattern I ship to production in 2026. No brokers in CI. No flaky tests.
Query Request → Kafka → Consumer → Result. Every hop is a test boundary.
Asynchronous events, partitioned ordering, shared clusters. Traditional integration tests break down.
Message delivery order isn't guaranteed across partitions. Tests that pass locally may fail in CI due to race conditions.
Running tests requires a Kafka cluster. This slows CI and introduces flakiness when clusters are shared.
How do you test what happens when a consumer crashes mid-batch? Or when Kafka is temporarily unavailable?
Consumers maintain offsets and local state. Replaying events requires careful setup and teardown to avoid pollution.
Embedded Kafka for integration tests. Forked simulation repos for deterministic replay.
Record real production events and replay them in tests. Validate consumer logic against actual payloads.
@EmbeddedKafka
@TestSend events to specific partitions to test ordering guarantees. Ensure same-key events are processed sequentially.
partition = hash(key) % 3Simulate broker failures, consumer crashes, and network partitions. Verify graceful degradation and retry logic.
kafkaServer.shutdown()How Kafka testing caught a race condition that would've caused incorrect query results
// Send 3 queries for same session to different partitions
sendQuery(sessionId="user-123", query="Show sales by region", partition=0);
sendQuery(sessionId="user-123", query="Filter to Q4 only", partition=1);
sendQuery(sessionId="user-123", query="Add year-over-year compare", partition=2);
// ❌ Assertion failed: queries processed out of order!hash(sessionId). Now all queries for the same user session go to the same partition, guaranteeing order.Control exact message order and timing. Tests that pass once will pass every time.
Simulate catastrophic failures without risking production data. Test disaster recovery procedures with confidence.
Embedded Kafka starts in seconds. Run full integration tests in CI without external dependencies.
The questions engineers actually ask when setting up Kafka tests in Spring Boot.
Use the @EmbeddedKafka annotation from spring-kafka-test to spin up an in-memory Kafka broker scoped to the test JVM. Combine it with @SpringBootTest and a fixed partition count. Produce messages with explicit partition targets so ordering is controlled, then assert on the consumer's state after a bounded wait. This removes broker shared-state flakiness and makes tests pass-once-pass-always.
Embedded Kafka is an in-process Kafka broker that starts inside the test JVM and shuts down when the test class finishes. Use it for integration tests where you need real serialization, partition assignment, and consumer group rebalancing — things Testcontainers or mocks cannot reproduce accurately. Use mocks only for pure unit tests on deserialization and business logic.
Produce messages with the same key and assert they land on the same partition (Kafka routes by hash(key) % partitions). Then consume with a single-threaded listener and assert the arrival order matches production order. For session-scoped ordering, use the session ID as the partition key so all events for one session serialize through one partition.
Call kafkaServer.shutdown() mid-test, assert the consumer detects the disconnection, then restart with kafkaServer.start() and assert the consumer resumes from the last committed offset. For network partitions, use Toxiproxy or the SimulatingNetworkFailures feature in spring-kafka-test. Verify retry counts, backoff timing, and dead-letter queue routing in the same test.
A Dead Letter Queue (DLQ) is a separate Kafka topic that receives messages the primary consumer failed to process after N retries. Test it by producing a poison message (wrong schema, business-rule violation), running the consumer, and asserting the message lands on the DLQ topic with the failure reason in the headers. Always test the DLQ consumer too — a DLQ nobody reads is worse than no DLQ.
Use embedded Kafka for fast JVM-scoped tests (sub-second startup). Switch to Testcontainers when you need to test schema registry, Kafka Connect, or vendor-specific features like Confluent Cloud. In 2026 most Spring Boot projects I ship use both: embedded Kafka for the 80% of tests that validate consumer logic, Testcontainers for the 20% that validate infrastructure integration.
Deeper dives into Spring Boot, Kafka, and production reliability on this site.