Skip to content

Latest commit

 

History

167 Commits

Folders and files

Repository files navigation

ktestify-cucumber

build passing license java 25 version docker


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.


Quick Start

Run with Docker (recommended)

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/features

Run with the fat JAR

java -Dconfig.file=src/test/resources/local.conf \
     -jar target/ktestify-cucumber.jar \
     src/test/resources/features

Writing Tests

Tests 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 Catalogue

Background (@Given)

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

Actions (@When)

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

Validations (@Then / @And)

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

All validation steps that accept an expectedRecordKey column also accept an optional keyMatchStrategy column. See Key matching strategies below.


Key matching strategies

When a step includes an expectedRecordKey column, you can optionally add a keyMatchStrategy column to control how the key is matched. This is useful when record keys are dynamically generated (UUIDs, prefixed keys) and exact match is too restrictive.

Strategy Value Behaviour
EXACT exact Exact string equality (default)
CONTAINS contains Expected key is a substring of actual
STARTS_WITH starts_with Actual key starts with expected
ENDS_WITH ends_with Actual key ends with expected
REGEX regex Actual key matches Java regex pattern
# Match a UUID-prefixed key without knowing the full UUID
Then expected record from file
  | topicAlias | file          | expectedRecordKey | keyMatchStrategy | consumerReadTimeout |
  | orders-out | expected.json | 550e8400          | starts_with      | 30                  |

The value is case-insensitive and tolerant of hyphens/underscores (starts-with, starts_with, StartsWith all work). Unrecognised values default to exact.


Batch matching strategies

When using batch validation steps (Then expected records from files or Then expected records from files based on schema), you can optionally include expectedRecordKeys (comma-separated keys paired 1-to-1 with files) and a batchMatchStrategy column to control how consumed records are paired with expected files and keys.

Strategy Accepted values Behaviour
POSITIONAL positional, ordered, index, strict Record at index i must match file at index i (and expectedRecordKeys[i] when set) (default)
LAZY lazy, fuzzy, unordered, any_order, any-order Order-independent 1-to-1 matching without replacement (matches both key and file when expectedRecordKeys is set)
# Positional (default): record 1 -> file 1, record 2 -> file 2, record 3 -> file 3
Then expected records from files
  | topicAlias | expectedRecordsCount | files                                                                                                 | expectedRecordKeys            | consumerReadTimeout | consumerDeltaTime |
  | batch-out  | 3                    | integration/batch-expected-1.json,integration/batch-expected-2.json,integration/batch-expected-3.json | batch-003,batch-004,batch-005 | 30                  | 60                |

# Lazy / Fuzzy: records sent in order (3, 1, 2) still match files listed as (1, 2, 3)
Then expected records from files
  | topicAlias | expectedRecordsCount | files                                                                                                 | expectedRecordKeys            | batchMatchStrategy | consumerReadTimeout | consumerDeltaTime |
  | batch-out  | 3                    | integration/batch-expected-1.json,integration/batch-expected-2.json,integration/batch-expected-3.json | batch-007,batch-008,batch-006 | lazy               | 30                  | 60                |

Configuration

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
    }
  }
}

Docker Image

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).


Dynamic Variables

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

Related


Contributing

Contributions are welcome. Please read the contributing guide before opening a pull request.

  1. Fork the repository
  2. Create a feature branch — git checkout -b feat/my-feature
  3. Commit with Conventional Commits — git commit -m "feat: add my feature"
  4. Push and open a Pull Request against main

License

ktestify-cucumber is licensed under the Apache License 2.0.


KTestify
Assert the stream. Own the pipeline.

Releases

Packages

Used by

Contributors

Languages