ktestify-cucumber is the standalone BDD test runner of the ktestify ecosystem. It wraps ktestify-core a Cucumber layer & Gherkin step definitions, PicoContainer dependency injection, and a ready-to-run Docker image, so you can write Kafka integration tests in plain English without a single line of Java.
docker run --rm \
-v $(pwd)/features:/workspace/features \
-v $(pwd)/assets:/workspace/assets \
-v $(pwd)/config:/workspace/config \
-v $(pwd)/reports:/workspace/reports \
-e KAFKA_BOOTSTRAP_SERVERS=kafka:9092 \
-e SCHEMA_REGISTRY_URL=http://schema-registry:8081 \
-e config.file=/workspace/config/application.conf \
ghcr.io/ktestify/ktestify-cucumber:latest \
/workspace/featuresjava -Dconfig.file=src/test/resources/local.conf \
-jar target/ktestify-cucumber.jar \
src/test/resources/featuresTests are written in standard Gherkin inside .feature files. A complete roundtrip example:
Feature: Order processing — raw roundtrip
Background:
Given namespace
| namespace |
| my-org |
Given input topic
| topicName | topicAlias |
| orders | orders-in |
Given output topic
| topicName | topicAlias |
| orders-processed | orders-out |
Given assets directory
| absolutePath |
| src/test/resources/data/orders |
Scenario: Processed order matches expected file
When record from file is sent
| topicName | file | recordKey |
| orders | order.json | ORD-001 |
Then expected record from file
| topicAlias | file | expectedRecordKey | consumerReadTimeout |
| orders-out | expected-order.json | ORD-001 | 30 || Step | Purpose |
|---|---|
Given namespace |
Register a single topic namespace |
Given namespaces |
Register multiple namespaces with aliases |
Given input topic |
Register a producer topic |
Given output topic |
Register a consumer topic |
Given assets directory |
Set the base path for payload/expected files |
Given schema |
Register an Avro schema (name + alias + version) |
Given input queue |
Register an IBM MQ input queue |
Given output queue |
Register an IBM MQ output queue |
Given CFT hosts |
Register a CFT file-transfer host |
| Step | Purpose |
|---|---|
When record from file is sent |
Produce a raw (String) record |
When record from file based on schema is sent |
Produce an Avro record |
When queue record from file is sent |
Send a message to an IBM MQ queue |
When file is sent using CFT |
Transfer a file via CFT |
And wait for {int} seconds |
Sleep between steps |
When script is executed |
Run a shell script |
| Step | Purpose |
|---|---|
Then expected record from file |
Raw file match |
Then expected record should have fields matching from file |
Positional field match (line/from/to) |
Then expected record from file based on XML |
XML structural match with optional exclusions |
Then expected record based on XML should have fields matching from file |
XPath expression match |
Then expected records from files |
Batch raw records vs. multiple files |
Then expected record from file based on schema |
Avro file match with optional excluded keys |
Then expected record based on schema should have fields matching from given value |
Avro field/value match |
Then expected records from files based on schema |
Batch Avro records |
And record should not appear in topic |
Negative assertion (no record within timeout) |
And record should appear in topic |
Positive presence assertion |
ktestify-cucumber uses Typesafe Config (HOCON). Override defaults via -Dconfig.file, environment variables, or an application.conf on the classpath.
ktestify {
kafka {
bootstrap-servers = "localhost:9092"
bootstrap-servers = ${?KAFKA_BOOTSTRAP_SERVERS}
topic-namespace = ${?KTESTIFY_TOPIC_NAMESPACE}
consumer.group-id = "my-test-group"
}
schema-registry {
url = "http://localhost:8081"
url = ${?SCHEMA_REGISTRY_URL}
}
framework {
timeouts {
default-read-timeout = 30s # cucumber default (longer than core's 10s)
consumer-delta-time = 60s
}
directories {
assets = "" # set via 'Given assets directory' or KTESTIFY_ASSETS_DIR
}
}
}The image is published to GitHub Container Registry on every release.
ghcr.io/ktestify/ktestify-cucumber:<version>
ghcr.io/ktestify/ktestify-cucumber:latest
| Mount | Default path inside container | Purpose |
|---|---|---|
| Feature files | /workspace/features |
Gherkin .feature files |
| Assets | /workspace/assets |
Payload and expected files |
| Config | /workspace/config |
application.conf (set -Dconfig.file) |
| Reports | /workspace/reports |
Cucumber HTML / JSON reports |
| Plugins | /workspace/plugins |
Drop-in plugin JARs |
The image supports linux/amd64 and linux/arm64 (Apple Silicon / cloud ARM).
Payload and expected files are processed transparently by DynamicVariableProcessor. Use {{VARIABLE}} syntax inside any file read by FileUtils.getFileContent().
| Syntax | Output example |
|---|---|
{{date}} |
2026-05-31 |
{{date:yyyy/MM/dd}} |
2026/05/31 |
{{timestamp}} |
2026-05-31T14:22:00 |
{{random}} |
UUID v4 |
{{random:str:12}} |
aB3xZ9qWmK1p |
{{env:MY_VAR}} |
value of $MY_VAR |
- ktestify-core — the transport and assertion engine
- docs.ktestify.xyz — full documentation and configuration reference
Contributions are welcome. Please read the contributing guide before opening a pull request.
- Fork the repository
- Create a feature branch —
git checkout -b feat/my-feature - Commit with Conventional Commits —
git commit -m "feat: add my feature" - Push and open a Pull Request against
main
ktestify-cucumber is licensed under the Apache License 2.0.
