Skip to main content

Architecture

KTestify is built on a strict three-layer separation of concerns. Each layer has one job and knows nothing about the other layers' implementation details.

ConsumedRecord<V> is the only data type that crosses layer boundaries.


Layer responsibilitiesโ€‹

Transport - RecordFetcher<V>โ€‹

Knows: Kafka broker, partitions, offsets, deduplication. Does NOT know: matchers, files, test frameworks.

The contract is a single interface:

public interface RecordFetcher<V> extends AutoCloseable {
List<ConsumedRecord<V>> fetch() throws FetchException;
void close();
}

Swapping Kafka for IBM MQ means writing a new IbmMqRecordFetcher<V>, nothing else changes.


Orchestration - AbstractKafkaConsumerโ€‹

Knows: fetch โ†’ match โ†’ result wiring. Does NOT know: Kafka internals, comparison algorithms.

// AbstractKafkaConsumer.call(), simplified
var fetcher = new KafkaRecordFetcher(context);
try {
List<ConsumedRecord<V>> records = fetcher.fetch(); // transport
MatchContext matchCtx = buildMatchContext();
MatchResult result = matcher.match(records, matchCtx); // assertion
return result.isPassed();
} catch (FetchException e) {
throw new ConsumerException(e.getMessage(), e);
} finally {
fetcher.close();
}

Assertion - RecordMatcher<V>โ€‹

Knows: ConsumedRecord, expected values, comparison algorithm. Does NOT know: Kafka, IBM MQ, any transport.

@FunctionalInterface
public interface RecordMatcher<V> {
MatchResult match(List<ConsumedRecord<V>> records, MatchContext context);
}

Module boundariesโ€‹

ktestify-core ktestify-cucumber
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
RecordFetcher<V> BackgroundStepDefinition
KafkaRecordFetcher โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ConsumerContext (config only)
AbstractKafkaConsumer ValidationStepDefinition
RecordMatcher<V> ConsumerValidationService
MatchContext / MatchResult
ConsumedRecord<V>

ktestify-cucumber must never import org.apache.kafka.*. It uses ConsumerContext / ProducerContext (ktestify-core abstractions) to configure the engine and receives only ConsumedRecord<V> back.


Class hierarchyโ€‹

Matchers hierarchyโ€‹

RecordMatcher<V> (@FunctionalInterface)
โ”œโ”€โ”€ NoOpRecordMatcher<V>
โ”œโ”€โ”€ FileRecordMatcher
โ”œโ”€โ”€ XmlRecordMatcher
โ”œโ”€โ”€ XPathRecordMatcher
โ”œโ”€โ”€ FieldsRecordMatcher
โ”œโ”€โ”€ FileKeyRecordMatcher
โ”œโ”€โ”€ KeyRecordMatcher
โ”œโ”€โ”€ AvroFileRecordMatcher
โ”œโ”€โ”€ AvroFileKeyRecordMatcher
โ”œโ”€โ”€ AvroFieldsRecordMatcher
โ””โ”€โ”€ AvroKeyRecordMatcher

See alsoโ€‹