All releases

v5.0.0

renamed-and-rewired

The library is now Wiretap: new coordinates, pass-through mode, and declarative fixture seeding.

Highlights

  • 01

    Wiretap, under new coordinates

    io.gitlab.guisseppe:spring-kafka-test-utils becomes io.typecheck:wiretap-spring-boot, with renamed packages, a renamed annotation (@EnableWiretapKafkaBroker) and a renamed property prefix (wiretap.test.*). Mechanical to migrate, and deliberately without compatibility shims.

    Read the guide →
  • 02

    Pass-through mode

    wiretap.test.mode=passthrough points the whole harness at a broker you already run — Testcontainers, docker compose, a local Kafka — while topics, seeding, listener readiness, assertions and per-method cleanup all keep working.

    Read the guide →
  • 03

    Declarative seeding with @KafkaSeed

    Declare the records a test starts from in a .json or .ndjson fixture, with ${placeholder} resolution, typed bindings and built-ins like ${#uuid} and ${#now-P1D}. Class seeds are re-applied after every cleanup.

    Read the guide →
  • 04

    Broker wiring survives a shadowing @ContextConfiguration

    A test class that declared its own @ContextConfiguration used to lose the entire broker wiring. A context customizer now applies the initializer additively, so the broker, the properties and the topics are there either way.

    Read the guide →

Migrate with AI

Paste this into your coding agent after installing the skill pack. It scopes the work to this release and keeps the agent on the documented API.

Prompt
You are migrating this repository from io.gitlab.guisseppe:spring-kafka-test-utils 4.x to io.typecheck:wiretap-spring-boot:5.0.0 (the same library, rebranded as wiretap). Make these changes:

1. Dependency: replace io.gitlab.guisseppe:spring-kafka-test-utils with io.typecheck:wiretap-spring-boot version 5.0.0 (test scope). Optionally import io.typecheck:wiretap-bom:5.0.0 in dependencyManagement.
2. Imports: io.gitlab.guisseppe.spring.kafka.test.utils.assertions.* -> io.typecheck.wiretap.core.assertions.*. Other io.gitlab.guisseppe.spring.kafka.test.utils.* -> io.typecheck.wiretap.core.* (KafkaTestPublisher, KafkaTopicReader, KafkaTestAwait, KafkaTestLatch, TopicCleaner) or io.typecheck.wiretap.spring.* (the annotation, KafkaTestListeners, and the renamed classes: SharedKafkaProperties -> WiretapTestProperties, SharedKafkaBrokerHolder -> WiretapBrokerHolder, SharedKafkaObjectMapper -> WiretapObjectMapper). The Jackson serializer pair is internal in both versions - nothing to migrate.
3. Annotation: @SharedKafkaBroker -> @EnableWiretapKafkaBroker (attributes topics() and partitions() unchanged).
4. Properties: rename every spring.kafka.test.<key> to wiretap.test.<key> (topics, topics-file, partitions, mock-schema-registry.enabled, object-mapper-bean-name, listeners.ready-timeout, bean-overrides.enabled, bean-overrides.consumer.auto-offset-reset, bean-overrides.consumer.group-id-suffix, bean-overrides.consumer.unique-group-ids). Do NOT touch Spring's own keys: spring.kafka.bootstrap-servers, any other spring.kafka.*, spring.embedded.kafka.brokers, logging.level.*.
5. Renamed literals (only if your tests reference them): object-mapper bean sharedKafkaTestObjectMapper -> wiretapTestObjectMapper; mock schema registry URL mock://shared-kafka-test -> mock://wiretap.
6. Removed API: overloads taking EmbeddedKafkaBroker on KafkaTestPublisher/KafkaTopicReader/KafkaAssertions are gone. Pass bootstrap servers explicitly (broker.getBrokersAsString()) or use the implicit no-servers forms. Keyless publish with explicit servers: KafkaTestPublisher.sendValue(servers, topic, value) or send(servers, topic, null, value). @Autowired EmbeddedKafkaBroker still works.
7. Verify: rg -n "io\.gitlab\.guisseppe|SharedKafkaBroker|spring\.kafka\.test\.|shared-kafka-test" src pom.xml build.gradle* must return nothing; rg -n "spring\.kafka\." should still show your unchanged Spring config. Run the full test suite.

Do it in one commit; 4.x and 5.0 cannot be mixed in one suite. After migrating, consider new 5.0.0 features: wiretap.test.mode=passthrough (run against Testcontainers or a local broker) and @KafkaSeed fixture seeding with ${placeholder} support.

Full notes

Breaking changes

  • Maven coordinates changed from io.gitlab.guisseppe:spring-kafka-test-utils to io.typecheck:wiretap-spring-boot. Releases up to 4.1.2 remain available under the old coordinates.
  • Packages renamed: io.gitlab.guisseppe.spring.kafka.test.utils.assertions.* to io.typecheck.wiretap.core.assertions.*, the rest of the engine to io.typecheck.wiretap.core.*, and the Spring integration (the annotation, KafkaTestListeners) to io.typecheck.wiretap.spring.*.
  • @SharedKafkaBroker renamed to @EnableWiretapKafkaBroker; its topics() and partitions() attributes are unchanged.
  • Every spring.kafka.test.<key> property renamed to wiretap.test.<key>. Spring's own keys — spring.kafka.*, spring.embedded.kafka.brokers, logging.level.* — are untouched.
  • Removed the overloads taking an EmbeddedKafkaBroker from KafkaTestPublisher, KafkaTopicReader and KafkaAssertions. Pass a bootstrap-servers address (broker.getBrokersAsString()) or use the implicit no-servers forms; injecting the EmbeddedKafkaBroker bean still works in embedded mode.
  • Renamed literals: the default object-mapper bean is wiretapTestObjectMapper (was sharedKafkaTestObjectMapper) and the mock schema registry URL is mock://wiretap (was mock://shared-kafka-test).
  • No compatibility shims are published: there is no deprecated annotation alias, no spring.kafka.test.* fallback and no re-exported packages, so 4.x and 5.0 cannot be mixed in one test suite.

Added

  • @KafkaSeed — repeatable declarative topic seeding on a test class or method, plus the suite-level wiretap.test.seed.locations and wiretap.test.seed.mode properties. Fixtures are .json arrays or .ndjson streams in an envelope (key/value/headers, including tombstones) or bare-value shorthand form. Adding avroSchema = "classpath:….avsc" publishes that seed's fixtures Avro-encoded against the mock schema registry: values convert from plain JSON to GenericRecords (unions written as the plain value or null, bytes/fixed base64, enum a symbol string, logical types passed through, schema defaults applied), while keys stay strings, headers are untouched and a null value is still a real tombstone. Suite-level seed locations stay JSON-only.
  • Placeholder resolution for seed fixtures: ${name}, inline defaults ${name:default}, the $${...} escape, and the built-ins ${#uuid}, ${#now}, ${#now±<ISO-8601 offset>} and ${#today} — resolved per occurrence, with whole-value bindings keeping their JSON type and SeedPlaceholders providers resolved as Spring beans when the context declares them.
  • Pass-through mode: wiretap.test.mode, wiretap.test.passthrough.bootstrap-servers and wiretap.test.passthrough.allow-remote run the harness against a cluster wiretap did not start, with no embedded broker, no spring.embedded.kafka.brokers and no JVM system properties published.
  • io.typecheck:wiretap-bom for aligning the versions of wiretap's own modules; it manages only wiretap artifacts, never third-party versions.
  • The build is now a monorepo of a Spring-free wiretap-core engine and the wiretap-spring-boot integration, so the publisher, reader, await helpers and assertions no longer depend on Spring.
  • KafkaTestPublisher.sendValue(bootstrapServers, topic, value) — the named form of a keyless publish at an explicit address, which the three-argument send signature cannot express.
  • An evaluation lane — a never-published wiretap-evaluation module that runs the harness against real Kafka brokers (Testcontainers locally, service containers in CI) across the supported broker lines, so divergence from a real cluster is caught in review.

Fixed

  • A test class that directly declares @ContextConfiguration no longer loses the broker wiring contributed by @EnableWiretapKafkaBroker. Such contexts previously came up with no broker bean, no spring.kafka.bootstrap-servers and no topics, and failed on the first Kafka interaction; a ContextCustomizerFactory now applies the initializer additively, and the initializer is idempotent when both routes fire.
  • Closed a race in the topic ledger where a topic could be treated as created while it was still being created. Creation now runs through a two-phase claim: a topic counts as created only once the creator commits, concurrent callers wait for that commit (in ascending name order, so overlapping topic sets cannot deadlock), a rolled-back claim returns the topic to absent, and the commit is recorded against the cluster address it happened on.
  • A test class whose own @ContextConfiguration shadows the annotation resolves its mode and pass-through address once, at wiring time. Supplying wiretap.test.mode or wiretap.test.passthrough.bootstrap-servers via @DynamicPropertySource on such a class now fails fast — those values arrive after the context customizer has already wired the broker — with a message pointing at @TestPropertySource, @SpringBootTest(properties) or application properties, rather than silently splitting the mode between components.

Security

  • Pass-through mode refuses a non-loopback target unless wiretap.test.passthrough.allow-remote=true. The guard is a restriction on the form of the configured address — localhost, any 127.0.0.0/8 address, ::1 — and performs no DNS resolution, deliberately, so its verdict depends on reviewable configuration rather than on the machine running the test.
  • Per-method cleanup on a cluster wiretap does not own is limited to the topics wiretap created or seeded on that cluster. Both halves of that ledger are recorded against the bootstrap address they happened on, and the cluster is never enumerated to discover what to delete.
  • Consumer-group deletion in pass-through mode is narrowed to groups carrying wiretap's unique group-id suffix; with suffixing disabled, no consumer group is deleted at all.