SlideShare a Scribd company logo
1 of 74
Download to read offline
TESTING KAFKA
 @maik_toepfer
OUTLINE
Introduction
Testing Endless
Test Approaches
Recommendations
ABOUT ME
software crafter by heart
working as a Software Engineer for PRISMA in Leipzig
organizer of the LE Software Craft Meetup
KAFKA IS…
a middleware
a distributed log
the hammer for all your nails 😏
KAFKA CAN BE…
…A CLASSIC MESSAGE QUEUE
App A
(consume)
Topic
write
App X
(produce)
read
…A PUBLISH-SUBSCRIBE SYSTEM
…WITH MULTIPLE CONSUMERS
consume
App A
App Z
consume
consume
Topic
App X
produce
App Y
…AND MULTIPLE PRODUCERS
App A
consume
App B
App Z
consume
App C
consume
Topic
produce
App X
produce
App Y
produce
THE DISTRIBUTED LOG
messages don’t vanish after being read
stored until they are purged (depending on retention policy)
INDEPENDENT CONSUMPTION OF MESSAGES IN A
TOPIC
Topic
(aka "the Log")
reads
writes
App X App Y
App A
reads
1
2
1
1
1
0
9876543210
READING ON
THE TEXTBOOK KAFKA CONSUMER
var props = new Properties();
props.put("bootstrap.servers", "kafka-server:9092");
props.put("key.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer");
var consumer = new KafkaConsumer<String, String>(props);
consumer.subscribe(List.of("my-topic"));
while (true) {
var records = consumer.poll(
Duration.ofMillis(Long.MAX_VALUE));
for (var record : records)
System.out.printf("offset = %d, key = %s, value = %s%n",
record.offset(), record.key(), record.value());
}
1 1
2
3
4
5
6
7
8
9 2
10
11 3
12
13 4
14
15
16
17
THE MESSAGE RECEIVER
too little creativity - sorry
OVERVIEW
Kafka Message Receiver Repository
read write
CLASS OVERVIEW
MessageReceiver
kafkaConsumer
topic
repository
run()
pass everything needed into the object
USAGE
public class Main {
public static void main(String[] args){
var consumer = new KafkaConsumer<String, String>(props);
var topic = "my-topic";
var repository = new Repository("jdbc:...");
var messageReceiver =
new MessageReceiver(consumer, topic, repository);
messageReceiver.run();
}
}
1
2
3
A LITTLE DETOUR
CONSTRUCTOR INJECTION
Kafka Consumer Topic Name
Message Receiver
Repository
providing everything the object requires via the constructor
enables testability
SIMPLE TO REPLACE DEPENDENCIES
Fake
Kafka Consumer Topic Name
Message Receiver
Fake
Repository
MORE ON DI (1)
MORE ON DI (2)
FIRST IMPLEMENTATION
public MessageReceiver(Consumer<String, String> kafkaConsumer,
String topic, Repository repository ) {
this.kafkaConsumer = kafkaConsumer;
this.topic = topic;
this.repository = repository;
}
public void run() {
kafkaConsumer.subscribe(List.of(topic));
while (true) { // this is an endless loop
var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE))
for (var record : records) {
repository.saveMessage(record.value());
}
}
}
1
HOW TO TEST "ENDLESS" ?
APPROACHES
1. Divide And Conquer
2. My Own De nition Of "Endless"
DIVIDE AND CONQUER
test everything - except the endless loop
DIVIDE AND CONQUER
public MessageReceiver(KafkaConsumer<String, String> kafkaConsumer,
String topic, Repository repository) {
this.kafkaConsumer = kafkaConsumer;
this.topic = topic;
this.repository = repository; }
public void run() {
kafkaConsumer.subscribe(List.of(topic));
while (true) {
processRecords();
}}
protected void processRecords() {
var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE));
for (var record : records) {
repository.saveMessage(record.value());
}}
A POSSIBLE TEST
@Mock Repository mockRepository;
@Mock KafkaConsumer<String,String> mockConsumer;
@Test
public void writesReceivedMessage() {
// arrange
var records = buildConsumerRecords("my-topic", "HELLO WORLD");
when(mockConsumer.poll(any())).thenAnswer(notUsed-> records);
// act (sut = System Under Test)
var sut = new MessageReceiver(mockConsumer, mockRepository, "my-topic")
// we don't call run() directly
sut.processRecords();
// assert
verify(mockRepository, times(1)).saveMessage("HELLO WORLD");
}
1
TEST COVERAGE
public MessageReceiver(KafkaConsumer<String, String> kafkaConsumer,
String topic, Repository repository) {
this.kafkaConsumer = kafkaConsumer;
this.topic = topic;
this.repository = repository; }
public void run() {
kafkaConsumer.subscribe(List.of(topic));
while (true) {
processRecords();
}}
protected void processRecords() {
var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE));
for (var record : records) {
repository.saveMessage(record.value());
}}
BUT WE CAN DO BETTER
THE MESSAGE RECEIVER REVISITED
What is upsetting here?
public MessageReceiver(Consumer<String, String> kafkaConsumer,
String topic, Repository repository ) {
this.kafkaConsumer = kafkaConsumer;
this.topic = topic;
this.repository = repository; }
public void run() {
kafkaConsumer.subscribe(List.of(topic));
while (true) {
var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE))
for (var record : records) {
repository.saveMessage(record.value());
}
}}
THE MESSAGE RECEIVER REVISITED
public MessageReceiver(Consumer<String, String> kafkaConsumer,
String topic, Repository repository ) {
this.kafkaConsumer = kafkaConsumer;
this.topic = topic;
this.repository = repository; }
public void run() {
kafkaConsumer.subscribe(List.of(topic));
while (true) { // <-- THIS !
var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE))
for (var record : records) {
repository.saveMessage(record.value());
}
}}
LET’S INJECT "ENDLESS"
public MessageReceiver(Consumer<String, String> kafkaConsumer,
String topic, Repository repository,
BooleanSupplier whileFunc ) {
this.kafkaConsumer = kafkaConsumer;
this.topic = topic;
this.repository = repository;
this.whileFunc = whileFunc; }
public void run() {
kafkaConsumer.subscribe(List.of(topic));
while (whileFunc.getAsBoolean()) {
var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE))
for (var record : records) {
repository.saveMessage(record.value()); }}}
LET’S INJECT "ENDLESS"
USAGE
public class Main {
public static void main(String[] args){
var consumer = new KafkaConsumer<String, String>(props);
var topic = "my-topic";
var repository = new Repository("jdbc:...");
var messageReceiver = new MessageReceiver(
consumer, topic, repository,
() -> true );
messageReceiver.run();
}
}
A POSSIBLE TEST
@Mock Repository mockRepository;
@Mock KafkaConsumer<String,String> mockConsumer;
@Test
public void writesReceivedMessage() {
// arrange
var records = buildConsumerRecords("my-topic", "HELLO WORLD");
when(mockConsumer.poll(any())).thenAnswer(notUsed-> records);
// act
var sut = new MessageReceiver(
mockConsumer, mockRepository, "my-topic",
this::runOnce);
sut.run();
// assert
verify(mockRepository, times(1)).saveMessage("HELLO WORLD");
}
ONCE IS ENOUGH
private boolean firstTimeRun = true;
private boolean runOnce() {
if (firstTimeRun) {
firstTimeRun = false;
return true;
}
return false;
}
LET’S LOOK AT THE TEST COVERAGE
TEST COVERAGE
public MessageReceiver(Consumer<String, String> kafkaConsumer,
String topic, Repository repository,
BooleanSupplier whileFunc ) {
this.kafkaConsumer = kafkaConsumer;
this.topic = topic;
this.repository = repository;
this.whileFunc = whileFunc;
}
public void run() {
kafkaConsumer.subscribe(List.of(topic));
while (whileFunc.getAsBoolean()) {
var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE))
for (var record : records) {
repository.saveMessage(record.value());
}
}
}
TSCHAKKA
TESTING KAFKA AT DIFFERENT INTEGRATION
LEVELS
OUR "SYSTEM UNDER TEST" (SUT)
public MessageReceiver(Consumer<String, String> kafkaConsumer,
String topic, Repository repository,
BooleanSupplier whileFunc ) {
this.kafkaConsumer = kafkaConsumer;
this.topic = topic;
this.repository = repository;
this.whileFunc = whileFunc; }
public void run() {
kafkaConsumer.subscribe(List.of(topic));
while (whileFunc.getAsBoolean()) {
var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE))
for (var record : records) {
repository.saveMessage(record.value());
}
}
}
MOCKITO
defacto standard for mocking in Java
MOCKITO - CONTEXT
MOCKITO - EXAMPLE TEST
@Mock Repository mockRepository;
@Mock KafkaConsumer<String,String> mockConsumer;
@Test
public void writesReceivedMessage() {
// arrange
var records = buildConsumerRecords("my-topic", "HELLO WORLD");
when(mockConsumer.poll(any())).thenAnswer(notUsed-> records);
// act
var sut = new MessageReceiver(
mockConsumer, mockRepository, "my-topic",
this::runOnce);
sut.run();
// assert
verify(mockRepository, times(1)).saveMessage("HELLO WORLD"); }
BORING
MOCKITO - PROS
execute fast
no Kafka needed
MOCKITO - CONS
maybe too isolated
KAFKA FAKES OBJECTS
fake objects
close to the actual prod implementation (our hope)
Kafka internally used for testing
FAKE OBJECT ?
Fake
Object
Test
Stub
Test
Double
Mock
Object
Complexity
Test
Spy
KAFKA FAKE OBJECTS - CONTEXT
USING THE PLAIN MOCKCONSUMER
import org.apache.kafka.clients.consumer.MockConsumer;
...
@Test
public void plainMockConsumer() {
// arrange
// preparing mock consumer
var mockConsumer =
new MockConsumer<String, String>(OffsetResetStrategy.EARLIEST);
// need to subscribe here, we'd like it in prod code ☹
mockConsumer.subscribe(List.of("my-topic"));
// voodoo part
var topicPartition=new TopicPartition("my-topic", 0);
mockConsumer.rebalance(List.of(topicPartition));
mockConsumer.updateBeginningOffsets(Map.of(topicPartition, 0L));
// this what we understand
mockConsumer.addRecord(new ConsumerRecord<>("my-topic",
0 0 ll "HELLO WORLD"))
THAT’S UGLY!
IDEA
Encapsulate the ugly part in a BufferedMockConsumer
BUFFERED MOCK CONSUMER
MockConsumer
BufferedMockConsumer
buffer() : called in test
subscribe() : called in prod code
poll() : called in prod code
inherits
50 lines of code
comes with the source of this presentation
UPDATED TEST
@Test
public void withBufferedMockConsumer() {
// arrange
var mockConsumer =
new BufferedMockConsumer<String, String>("my-topic");
mockConsumer.buffer("HELLO WORLD");
// act
var sut = new MessageReceiver(mockConsumer, "my-topic",
mockRepository, this::runOnce);
sut.run();
// assert
verify(mockRepository, times(1)).saveMessage("HELLO WORLD");
}
KAFKA FAKE OBJECTS - PROS
deeper Kafka testing
fake objects are provided by Kafka project team
should be closer to the real thing then Mockito
KAFKA FAKE OBJECTS - CONS
usually requires own helper class
EMBEDDED KAFKA
use a real Kafka cluster in your test
started inside the test
ALTERNATIVES
embedded-kafka (Scala)
Spring-Kafka
EMBEDDED KAFKA - CONTEXT
EMBEDDED KAFKA - EXAMPLE TEST
@Mock Repository mockRepository;
@RegisterExtension
public static final SharedKafkaTestResource sharedKafkaTestResource =
new SharedKafkaTestResource();
@Test
public void withEmbeddedKafkaCluster() {
// arrange
var kafkaTestUtils = sharedKafkaTestResource.getKafkaTestUtils();
var testRecord = Map.of("".getBytes(), "HELLO WORLD".getBytes());
kafkaTestUtils.produceRecords(testRecord, "my-topic", 0);
var props = new Properties();
props.put("group.id", "mygroup");
props.put("auto.offset.reset", "earliest"); // this one is needed
var consumer = kafkaTestUtils.getKafkaConsumer(
StringDeserializer.class, StringDeserializer.class, props);
// t
EMBEDDED KAFKA - PROS
you test with the real thing
no mocks
very close to production
EMBEDDED KAFKA - CONS
shared cluster between tests → no independent tests
in case of an error you deal with a real cluster
slow
TESTCONTAINERS
…a Java library that supports JUnit tests, providing lightweight,
throwaway instances of … anything else that can run in a Docker
container.
TESTCONTAINERS - CONTEXT
TESTCONTAINERS - EXAMPLE TEST
@Mock Repository mockRepository;
@Container
private static final KafkaContainer KAFKA = new KafkaContainer();
@Test
public void withTestcontainers() {
// arrange
var producerProps = new Properties();
producerProps.put("bootstrap.servers", KAFKA.getBootstrapServers());
producerProps.put("key.serializer", StringSerializer.class);
producerProps.put("value.serializer", StringSerializer.class);
var consumerProps = new Properties();
consumerProps.put("bootstrap.servers", KAFKA.getBootstrapServers());
consumerProps.put("group.id", "mygroup");
consumerProps.put("auto.offset.reset", "earliest");
consumerProps.put("key.deserializer", StringDeserializer.class);
consumerProps.put("value.deserializer", StringDeserializer.class);
K fk C St i St i ( P )
TESTCONTAINERS - PROS
easy to use
generally good for integration tests (e.g. database)
project has currently a lot of traction
TESTCONTAINERS - CONS
requires Docker as a separate dependency
your CI needs to support Docker as well
slow
RECOMMENDATIONS
RECALL THE TEST PYRAMID
many tests on unit level
little test on end-to-end level
OUR APPROACH
majority: Kafka Fake objects combined with an own
BufferedMockConsumer
a couple of integration tests based on Testcontainers
THANK YOU FOR YOUR ATTENTION!
IMAGE REFERENCES
[tired] https://pixabay.com/photos/winks-sleeping-snooze-sleep-2383407/
[maki] https://pixabay.com/photos/maki-curious-halfaap-peekaboo-3295891/
[boring] https://pixabay.com/photos/cute-animal-pet-tired-yawn-sleep-3258931/
[balled_up_paper] https://unsplash.com/photos/e4Davs7-XnE
[detour] https://unsplash.com/photos/dQLgop4tnsc
[endless] https://pixabay.com/de/photos/cassiopeia-supernova-cassiopeia-2515913/
[craft] https://pixabay.com/de/photos/hobel-hammer-s%C3%A4ge-rost-n%C3%A4gel-4607203/

More Related Content

What's hot

Cypress Automation Testing Tutorial (Part 1).pdf
Cypress Automation Testing Tutorial (Part 1).pdfCypress Automation Testing Tutorial (Part 1).pdf
Cypress Automation Testing Tutorial (Part 1).pdfbacancytechnology
 
Cypress - Best Practices
Cypress - Best PracticesCypress - Best Practices
Cypress - Best PracticesBrian Mann
 
PromQL Deep Dive - The Prometheus Query Language
PromQL Deep Dive - The Prometheus Query Language PromQL Deep Dive - The Prometheus Query Language
PromQL Deep Dive - The Prometheus Query Language Weaveworks
 
Garbage First Garbage Collector (G1 GC) - Migration to, Expectations and Adva...
Garbage First Garbage Collector (G1 GC) - Migration to, Expectations and Adva...Garbage First Garbage Collector (G1 GC) - Migration to, Expectations and Adva...
Garbage First Garbage Collector (G1 GC) - Migration to, Expectations and Adva...Monica Beckwith
 
Apache Camel K - Copenhagen v2
Apache Camel K - Copenhagen v2Apache Camel K - Copenhagen v2
Apache Camel K - Copenhagen v2Claus Ibsen
 
Discover Quarkus and GraalVM
Discover Quarkus and GraalVMDiscover Quarkus and GraalVM
Discover Quarkus and GraalVMRomain Schlick
 
Prometheus Multi Tenancy
Prometheus Multi TenancyPrometheus Multi Tenancy
Prometheus Multi TenancyNatan Yellin
 
Kubernetes extensibility: CRDs & Operators
Kubernetes extensibility: CRDs & OperatorsKubernetes extensibility: CRDs & Operators
Kubernetes extensibility: CRDs & OperatorsSIGHUP
 
Fitnesse - Acceptance testing
Fitnesse - Acceptance testingFitnesse - Acceptance testing
Fitnesse - Acceptance testingvijay_challa
 
An Introduction To Automated API Testing
An Introduction To Automated API TestingAn Introduction To Automated API Testing
An Introduction To Automated API TestingSauce Labs
 
Automation Tools Overview
Automation Tools OverviewAutomation Tools Overview
Automation Tools OverviewMurageppa-QA
 
JMeter Load Testing | Load Testing Using JMmeter | JMeter Tutorial For Beginn...
JMeter Load Testing | Load Testing Using JMmeter | JMeter Tutorial For Beginn...JMeter Load Testing | Load Testing Using JMmeter | JMeter Tutorial For Beginn...
JMeter Load Testing | Load Testing Using JMmeter | JMeter Tutorial For Beginn...Simplilearn
 
NashTech - Azure Application Insights
NashTech - Azure Application InsightsNashTech - Azure Application Insights
NashTech - Azure Application InsightsPhi Huynh
 
Full-Stack Development with Spring Boot and VueJS
Full-Stack Development with Spring Boot and VueJSFull-Stack Development with Spring Boot and VueJS
Full-Stack Development with Spring Boot and VueJSVMware Tanzu
 
Automate REST API Testing
Automate REST API TestingAutomate REST API Testing
Automate REST API TestingTechWell
 
Jenkins tutorial for beginners
Jenkins tutorial for beginnersJenkins tutorial for beginners
Jenkins tutorial for beginnersBugRaptors
 
오픈스택 커뮤니티 소개 및 기술 동향
오픈스택 커뮤니티 소개 및 기술 동향오픈스택 커뮤니티 소개 및 기술 동향
오픈스택 커뮤니티 소개 및 기술 동향Nalee Jang
 

What's hot (20)

Cypress Automation Testing Tutorial (Part 1).pdf
Cypress Automation Testing Tutorial (Part 1).pdfCypress Automation Testing Tutorial (Part 1).pdf
Cypress Automation Testing Tutorial (Part 1).pdf
 
Cypress - Best Practices
Cypress - Best PracticesCypress - Best Practices
Cypress - Best Practices
 
PromQL Deep Dive - The Prometheus Query Language
PromQL Deep Dive - The Prometheus Query Language PromQL Deep Dive - The Prometheus Query Language
PromQL Deep Dive - The Prometheus Query Language
 
Garbage First Garbage Collector (G1 GC) - Migration to, Expectations and Adva...
Garbage First Garbage Collector (G1 GC) - Migration to, Expectations and Adva...Garbage First Garbage Collector (G1 GC) - Migration to, Expectations and Adva...
Garbage First Garbage Collector (G1 GC) - Migration to, Expectations and Adva...
 
Apache Camel K - Copenhagen v2
Apache Camel K - Copenhagen v2Apache Camel K - Copenhagen v2
Apache Camel K - Copenhagen v2
 
Discover Quarkus and GraalVM
Discover Quarkus and GraalVMDiscover Quarkus and GraalVM
Discover Quarkus and GraalVM
 
Docker.pptx
Docker.pptxDocker.pptx
Docker.pptx
 
Prometheus Multi Tenancy
Prometheus Multi TenancyPrometheus Multi Tenancy
Prometheus Multi Tenancy
 
ELK Stack
ELK StackELK Stack
ELK Stack
 
Kubernetes extensibility: CRDs & Operators
Kubernetes extensibility: CRDs & OperatorsKubernetes extensibility: CRDs & Operators
Kubernetes extensibility: CRDs & Operators
 
Fitnesse - Acceptance testing
Fitnesse - Acceptance testingFitnesse - Acceptance testing
Fitnesse - Acceptance testing
 
An Introduction To Automated API Testing
An Introduction To Automated API TestingAn Introduction To Automated API Testing
An Introduction To Automated API Testing
 
Automation Tools Overview
Automation Tools OverviewAutomation Tools Overview
Automation Tools Overview
 
JMeter Load Testing | Load Testing Using JMmeter | JMeter Tutorial For Beginn...
JMeter Load Testing | Load Testing Using JMmeter | JMeter Tutorial For Beginn...JMeter Load Testing | Load Testing Using JMmeter | JMeter Tutorial For Beginn...
JMeter Load Testing | Load Testing Using JMmeter | JMeter Tutorial For Beginn...
 
Jenkins Pipelines
Jenkins PipelinesJenkins Pipelines
Jenkins Pipelines
 
NashTech - Azure Application Insights
NashTech - Azure Application InsightsNashTech - Azure Application Insights
NashTech - Azure Application Insights
 
Full-Stack Development with Spring Boot and VueJS
Full-Stack Development with Spring Boot and VueJSFull-Stack Development with Spring Boot and VueJS
Full-Stack Development with Spring Boot and VueJS
 
Automate REST API Testing
Automate REST API TestingAutomate REST API Testing
Automate REST API Testing
 
Jenkins tutorial for beginners
Jenkins tutorial for beginnersJenkins tutorial for beginners
Jenkins tutorial for beginners
 
오픈스택 커뮤니티 소개 및 기술 동향
오픈스택 커뮤니티 소개 및 기술 동향오픈스택 커뮤니티 소개 및 기술 동향
오픈스택 커뮤니티 소개 및 기술 동향
 

Similar to Testing Kafka - The Developer Perspective

Streaming Design Patterns Using Alpakka Kafka Connector (Sean Glover, Lightbe...
Streaming Design Patterns Using Alpakka Kafka Connector (Sean Glover, Lightbe...Streaming Design Patterns Using Alpakka Kafka Connector (Sean Glover, Lightbe...
Streaming Design Patterns Using Alpakka Kafka Connector (Sean Glover, Lightbe...confluent
 
Everything you wanted to know about writing async, concurrent http apps in java
Everything you wanted to know about writing async, concurrent http apps in java Everything you wanted to know about writing async, concurrent http apps in java
Everything you wanted to know about writing async, concurrent http apps in java Baruch Sadogursky
 
A new execution model for Nashorn in Java 9
A new execution model for Nashorn in Java 9A new execution model for Nashorn in Java 9
A new execution model for Nashorn in Java 9Marcus Lagergren
 
Patterns for JVM languages JokerConf
Patterns for JVM languages JokerConfPatterns for JVM languages JokerConf
Patterns for JVM languages JokerConfJaroslaw Palka
 
TDC2016POA | Trilha Arquitetura - Apache Kafka: uma introdução a logs distrib...
TDC2016POA | Trilha Arquitetura - Apache Kafka: uma introdução a logs distrib...TDC2016POA | Trilha Arquitetura - Apache Kafka: uma introdução a logs distrib...
TDC2016POA | Trilha Arquitetura - Apache Kafka: uma introdução a logs distrib...tdc-globalcode
 
Streaming twitter data using kafka
Streaming twitter data using kafkaStreaming twitter data using kafka
Streaming twitter data using kafkaKiran Krishna
 
Virtual Bash! A Lunchtime Introduction to Kafka
Virtual Bash! A Lunchtime Introduction to KafkaVirtual Bash! A Lunchtime Introduction to Kafka
Virtual Bash! A Lunchtime Introduction to KafkaJason Bell
 
I can't believe it's not a queue: Kafka and Spring
I can't believe it's not a queue: Kafka and SpringI can't believe it's not a queue: Kafka and Spring
I can't believe it's not a queue: Kafka and SpringJoe Kutner
 
Paris Kafka Meetup - How to develop with Kafka
Paris Kafka Meetup - How to develop with KafkaParis Kafka Meetup - How to develop with Kafka
Paris Kafka Meetup - How to develop with KafkaFlorian Hussonnois
 
The Wonderful World of Apache Kafka®
The Wonderful World of Apache Kafka®The Wonderful World of Apache Kafka®
The Wonderful World of Apache Kafka®confluent
 
[2019] 바르게, 빠르게! Reactive를 품은 Spring Kafka
[2019] 바르게, 빠르게! Reactive를 품은 Spring Kafka[2019] 바르게, 빠르게! Reactive를 품은 Spring Kafka
[2019] 바르게, 빠르게! Reactive를 품은 Spring KafkaNHN FORWARD
 
From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...
From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...
From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...Databricks
 
Java Libraries You Can’t Afford to Miss
Java Libraries You Can’t Afford to MissJava Libraries You Can’t Afford to Miss
Java Libraries You Can’t Afford to MissAndres Almiray
 
Elasticsearch And Apache Lucene For Apache Spark And MLlib
Elasticsearch And Apache Lucene For Apache Spark And MLlibElasticsearch And Apache Lucene For Apache Spark And MLlib
Elasticsearch And Apache Lucene For Apache Spark And MLlibJen Aman
 
Writing Continuous Applications with Structured Streaming Python APIs in Apac...
Writing Continuous Applications with Structured Streaming Python APIs in Apac...Writing Continuous Applications with Structured Streaming Python APIs in Apac...
Writing Continuous Applications with Structured Streaming Python APIs in Apac...Databricks
 
Leveraging Azure Databricks to minimize time to insight by combining Batch an...
Leveraging Azure Databricks to minimize time to insight by combining Batch an...Leveraging Azure Databricks to minimize time to insight by combining Batch an...
Leveraging Azure Databricks to minimize time to insight by combining Batch an...Microsoft Tech Community
 

Similar to Testing Kafka - The Developer Perspective (20)

Streaming Design Patterns Using Alpakka Kafka Connector (Sean Glover, Lightbe...
Streaming Design Patterns Using Alpakka Kafka Connector (Sean Glover, Lightbe...Streaming Design Patterns Using Alpakka Kafka Connector (Sean Glover, Lightbe...
Streaming Design Patterns Using Alpakka Kafka Connector (Sean Glover, Lightbe...
 
Everything you wanted to know about writing async, concurrent http apps in java
Everything you wanted to know about writing async, concurrent http apps in java Everything you wanted to know about writing async, concurrent http apps in java
Everything you wanted to know about writing async, concurrent http apps in java
 
KAFKA Quickstart
KAFKA QuickstartKAFKA Quickstart
KAFKA Quickstart
 
A new execution model for Nashorn in Java 9
A new execution model for Nashorn in Java 9A new execution model for Nashorn in Java 9
A new execution model for Nashorn in Java 9
 
Patterns for JVM languages JokerConf
Patterns for JVM languages JokerConfPatterns for JVM languages JokerConf
Patterns for JVM languages JokerConf
 
TDC2016POA | Trilha Arquitetura - Apache Kafka: uma introdução a logs distrib...
TDC2016POA | Trilha Arquitetura - Apache Kafka: uma introdução a logs distrib...TDC2016POA | Trilha Arquitetura - Apache Kafka: uma introdução a logs distrib...
TDC2016POA | Trilha Arquitetura - Apache Kafka: uma introdução a logs distrib...
 
Streaming twitter data using kafka
Streaming twitter data using kafkaStreaming twitter data using kafka
Streaming twitter data using kafka
 
Virtual Bash! A Lunchtime Introduction to Kafka
Virtual Bash! A Lunchtime Introduction to KafkaVirtual Bash! A Lunchtime Introduction to Kafka
Virtual Bash! A Lunchtime Introduction to Kafka
 
I can't believe it's not a queue: Kafka and Spring
I can't believe it's not a queue: Kafka and SpringI can't believe it's not a queue: Kafka and Spring
I can't believe it's not a queue: Kafka and Spring
 
Paris Kafka Meetup - How to develop with Kafka
Paris Kafka Meetup - How to develop with KafkaParis Kafka Meetup - How to develop with Kafka
Paris Kafka Meetup - How to develop with Kafka
 
The Wonderful World of Apache Kafka®
The Wonderful World of Apache Kafka®The Wonderful World of Apache Kafka®
The Wonderful World of Apache Kafka®
 
[2019] 바르게, 빠르게! Reactive를 품은 Spring Kafka
[2019] 바르게, 빠르게! Reactive를 품은 Spring Kafka[2019] 바르게, 빠르게! Reactive를 품은 Spring Kafka
[2019] 바르게, 빠르게! Reactive를 품은 Spring Kafka
 
From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...
From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...
From HelloWorld to Configurable and Reusable Apache Spark Applications in Sca...
 
Java Libraries You Can’t Afford to Miss
Java Libraries You Can’t Afford to MissJava Libraries You Can’t Afford to Miss
Java Libraries You Can’t Afford to Miss
 
Resilience mit Hystrix
Resilience mit HystrixResilience mit Hystrix
Resilience mit Hystrix
 
Kotlin Coroutines and Rx
Kotlin Coroutines and RxKotlin Coroutines and Rx
Kotlin Coroutines and Rx
 
What`s new in Java 7
What`s new in Java 7What`s new in Java 7
What`s new in Java 7
 
Elasticsearch And Apache Lucene For Apache Spark And MLlib
Elasticsearch And Apache Lucene For Apache Spark And MLlibElasticsearch And Apache Lucene For Apache Spark And MLlib
Elasticsearch And Apache Lucene For Apache Spark And MLlib
 
Writing Continuous Applications with Structured Streaming Python APIs in Apac...
Writing Continuous Applications with Structured Streaming Python APIs in Apac...Writing Continuous Applications with Structured Streaming Python APIs in Apac...
Writing Continuous Applications with Structured Streaming Python APIs in Apac...
 
Leveraging Azure Databricks to minimize time to insight by combining Batch an...
Leveraging Azure Databricks to minimize time to insight by combining Batch an...Leveraging Azure Databricks to minimize time to insight by combining Batch an...
Leveraging Azure Databricks to minimize time to insight by combining Batch an...
 

Recently uploaded

XpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software SolutionsXpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software SolutionsMehedi Hasan Shohan
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyFrank van der Linden
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxTier1 app
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...aditisharan08
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number SystemsJheuzeDellosa
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataBradBedford3
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationkaushalgiri8080
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfPower Karaoke
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 

Recently uploaded (20)

XpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software SolutionsXpertSolvers: Your Partner in Building Innovative Software Solutions
XpertSolvers: Your Partner in Building Innovative Software Solutions
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The Ugly
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number Systems
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
Project Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanationProject Based Learning (A.I).pptx detail explanation
Project Based Learning (A.I).pptx detail explanation
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
The Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdfThe Evolution of Karaoke From Analog to App.pdf
The Evolution of Karaoke From Analog to App.pdf
 
Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 

Testing Kafka - The Developer Perspective

  • 3. ABOUT ME software crafter by heart working as a Software Engineer for PRISMA in Leipzig organizer of the LE Software Craft Meetup
  • 4. KAFKA IS… a middleware a distributed log the hammer for all your nails 😏
  • 6. …A CLASSIC MESSAGE QUEUE App A (consume) Topic write App X (produce) read
  • 8. …WITH MULTIPLE CONSUMERS consume App A App Z consume consume Topic App X produce App Y
  • 9. …AND MULTIPLE PRODUCERS App A consume App B App Z consume App C consume Topic produce App X produce App Y produce
  • 10. THE DISTRIBUTED LOG messages don’t vanish after being read stored until they are purged (depending on retention policy)
  • 11. INDEPENDENT CONSUMPTION OF MESSAGES IN A TOPIC Topic (aka "the Log") reads writes App X App Y App A reads 1 2 1 1 1 0 9876543210
  • 13. THE TEXTBOOK KAFKA CONSUMER var props = new Properties(); props.put("bootstrap.servers", "kafka-server:9092"); props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); var consumer = new KafkaConsumer<String, String>(props); consumer.subscribe(List.of("my-topic")); while (true) { var records = consumer.poll( Duration.ofMillis(Long.MAX_VALUE)); for (var record : records) System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value()); } 1 1 2 3 4 5 6 7 8 9 2 10 11 3 12 13 4 14 15 16 17
  • 14. THE MESSAGE RECEIVER too little creativity - sorry
  • 15. OVERVIEW Kafka Message Receiver Repository read write
  • 17. USAGE public class Main { public static void main(String[] args){ var consumer = new KafkaConsumer<String, String>(props); var topic = "my-topic"; var repository = new Repository("jdbc:..."); var messageReceiver = new MessageReceiver(consumer, topic, repository); messageReceiver.run(); } } 1 2 3
  • 19. CONSTRUCTOR INJECTION Kafka Consumer Topic Name Message Receiver Repository providing everything the object requires via the constructor enables testability
  • 20. SIMPLE TO REPLACE DEPENDENCIES Fake Kafka Consumer Topic Name Message Receiver Fake Repository
  • 21. MORE ON DI (1)
  • 22. MORE ON DI (2)
  • 23. FIRST IMPLEMENTATION public MessageReceiver(Consumer<String, String> kafkaConsumer, String topic, Repository repository ) { this.kafkaConsumer = kafkaConsumer; this.topic = topic; this.repository = repository; } public void run() { kafkaConsumer.subscribe(List.of(topic)); while (true) { // this is an endless loop var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE)) for (var record : records) { repository.saveMessage(record.value()); } } } 1
  • 24. HOW TO TEST "ENDLESS" ?
  • 25. APPROACHES 1. Divide And Conquer 2. My Own De nition Of "Endless"
  • 26. DIVIDE AND CONQUER test everything - except the endless loop
  • 27. DIVIDE AND CONQUER public MessageReceiver(KafkaConsumer<String, String> kafkaConsumer, String topic, Repository repository) { this.kafkaConsumer = kafkaConsumer; this.topic = topic; this.repository = repository; } public void run() { kafkaConsumer.subscribe(List.of(topic)); while (true) { processRecords(); }} protected void processRecords() { var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE)); for (var record : records) { repository.saveMessage(record.value()); }}
  • 28. A POSSIBLE TEST @Mock Repository mockRepository; @Mock KafkaConsumer<String,String> mockConsumer; @Test public void writesReceivedMessage() { // arrange var records = buildConsumerRecords("my-topic", "HELLO WORLD"); when(mockConsumer.poll(any())).thenAnswer(notUsed-> records); // act (sut = System Under Test) var sut = new MessageReceiver(mockConsumer, mockRepository, "my-topic") // we don't call run() directly sut.processRecords(); // assert verify(mockRepository, times(1)).saveMessage("HELLO WORLD"); } 1
  • 29. TEST COVERAGE public MessageReceiver(KafkaConsumer<String, String> kafkaConsumer, String topic, Repository repository) { this.kafkaConsumer = kafkaConsumer; this.topic = topic; this.repository = repository; } public void run() { kafkaConsumer.subscribe(List.of(topic)); while (true) { processRecords(); }} protected void processRecords() { var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE)); for (var record : records) { repository.saveMessage(record.value()); }}
  • 30. BUT WE CAN DO BETTER
  • 31. THE MESSAGE RECEIVER REVISITED What is upsetting here? public MessageReceiver(Consumer<String, String> kafkaConsumer, String topic, Repository repository ) { this.kafkaConsumer = kafkaConsumer; this.topic = topic; this.repository = repository; } public void run() { kafkaConsumer.subscribe(List.of(topic)); while (true) { var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE)) for (var record : records) { repository.saveMessage(record.value()); } }}
  • 32. THE MESSAGE RECEIVER REVISITED public MessageReceiver(Consumer<String, String> kafkaConsumer, String topic, Repository repository ) { this.kafkaConsumer = kafkaConsumer; this.topic = topic; this.repository = repository; } public void run() { kafkaConsumer.subscribe(List.of(topic)); while (true) { // <-- THIS ! var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE)) for (var record : records) { repository.saveMessage(record.value()); } }}
  • 33. LET’S INJECT "ENDLESS" public MessageReceiver(Consumer<String, String> kafkaConsumer, String topic, Repository repository, BooleanSupplier whileFunc ) { this.kafkaConsumer = kafkaConsumer; this.topic = topic; this.repository = repository; this.whileFunc = whileFunc; } public void run() { kafkaConsumer.subscribe(List.of(topic)); while (whileFunc.getAsBoolean()) { var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE)) for (var record : records) { repository.saveMessage(record.value()); }}}
  • 35. USAGE public class Main { public static void main(String[] args){ var consumer = new KafkaConsumer<String, String>(props); var topic = "my-topic"; var repository = new Repository("jdbc:..."); var messageReceiver = new MessageReceiver( consumer, topic, repository, () -> true ); messageReceiver.run(); } }
  • 36. A POSSIBLE TEST @Mock Repository mockRepository; @Mock KafkaConsumer<String,String> mockConsumer; @Test public void writesReceivedMessage() { // arrange var records = buildConsumerRecords("my-topic", "HELLO WORLD"); when(mockConsumer.poll(any())).thenAnswer(notUsed-> records); // act var sut = new MessageReceiver( mockConsumer, mockRepository, "my-topic", this::runOnce); sut.run(); // assert verify(mockRepository, times(1)).saveMessage("HELLO WORLD"); }
  • 37. ONCE IS ENOUGH private boolean firstTimeRun = true; private boolean runOnce() { if (firstTimeRun) { firstTimeRun = false; return true; } return false; }
  • 38. LET’S LOOK AT THE TEST COVERAGE
  • 39. TEST COVERAGE public MessageReceiver(Consumer<String, String> kafkaConsumer, String topic, Repository repository, BooleanSupplier whileFunc ) { this.kafkaConsumer = kafkaConsumer; this.topic = topic; this.repository = repository; this.whileFunc = whileFunc; } public void run() { kafkaConsumer.subscribe(List.of(topic)); while (whileFunc.getAsBoolean()) { var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE)) for (var record : records) { repository.saveMessage(record.value()); } } }
  • 41. TESTING KAFKA AT DIFFERENT INTEGRATION LEVELS
  • 42. OUR "SYSTEM UNDER TEST" (SUT) public MessageReceiver(Consumer<String, String> kafkaConsumer, String topic, Repository repository, BooleanSupplier whileFunc ) { this.kafkaConsumer = kafkaConsumer; this.topic = topic; this.repository = repository; this.whileFunc = whileFunc; } public void run() { kafkaConsumer.subscribe(List.of(topic)); while (whileFunc.getAsBoolean()) { var records = kafkaConsumer.poll(Duration.ofMillis(Long.MAX_VALUE)) for (var record : records) { repository.saveMessage(record.value()); } } }
  • 43. MOCKITO defacto standard for mocking in Java
  • 45. MOCKITO - EXAMPLE TEST @Mock Repository mockRepository; @Mock KafkaConsumer<String,String> mockConsumer; @Test public void writesReceivedMessage() { // arrange var records = buildConsumerRecords("my-topic", "HELLO WORLD"); when(mockConsumer.poll(any())).thenAnswer(notUsed-> records); // act var sut = new MessageReceiver( mockConsumer, mockRepository, "my-topic", this::runOnce); sut.run(); // assert verify(mockRepository, times(1)).saveMessage("HELLO WORLD"); }
  • 47. MOCKITO - PROS execute fast no Kafka needed
  • 48. MOCKITO - CONS maybe too isolated
  • 49. KAFKA FAKES OBJECTS fake objects close to the actual prod implementation (our hope) Kafka internally used for testing
  • 51. KAFKA FAKE OBJECTS - CONTEXT
  • 52. USING THE PLAIN MOCKCONSUMER import org.apache.kafka.clients.consumer.MockConsumer; ... @Test public void plainMockConsumer() { // arrange // preparing mock consumer var mockConsumer = new MockConsumer<String, String>(OffsetResetStrategy.EARLIEST); // need to subscribe here, we'd like it in prod code ☹ mockConsumer.subscribe(List.of("my-topic")); // voodoo part var topicPartition=new TopicPartition("my-topic", 0); mockConsumer.rebalance(List.of(topicPartition)); mockConsumer.updateBeginningOffsets(Map.of(topicPartition, 0L)); // this what we understand mockConsumer.addRecord(new ConsumerRecord<>("my-topic", 0 0 ll "HELLO WORLD"))
  • 54. IDEA Encapsulate the ugly part in a BufferedMockConsumer
  • 55. BUFFERED MOCK CONSUMER MockConsumer BufferedMockConsumer buffer() : called in test subscribe() : called in prod code poll() : called in prod code inherits 50 lines of code comes with the source of this presentation
  • 56. UPDATED TEST @Test public void withBufferedMockConsumer() { // arrange var mockConsumer = new BufferedMockConsumer<String, String>("my-topic"); mockConsumer.buffer("HELLO WORLD"); // act var sut = new MessageReceiver(mockConsumer, "my-topic", mockRepository, this::runOnce); sut.run(); // assert verify(mockRepository, times(1)).saveMessage("HELLO WORLD"); }
  • 57. KAFKA FAKE OBJECTS - PROS deeper Kafka testing fake objects are provided by Kafka project team should be closer to the real thing then Mockito
  • 58. KAFKA FAKE OBJECTS - CONS usually requires own helper class
  • 59. EMBEDDED KAFKA use a real Kafka cluster in your test started inside the test
  • 61. EMBEDDED KAFKA - CONTEXT
  • 62. EMBEDDED KAFKA - EXAMPLE TEST @Mock Repository mockRepository; @RegisterExtension public static final SharedKafkaTestResource sharedKafkaTestResource = new SharedKafkaTestResource(); @Test public void withEmbeddedKafkaCluster() { // arrange var kafkaTestUtils = sharedKafkaTestResource.getKafkaTestUtils(); var testRecord = Map.of("".getBytes(), "HELLO WORLD".getBytes()); kafkaTestUtils.produceRecords(testRecord, "my-topic", 0); var props = new Properties(); props.put("group.id", "mygroup"); props.put("auto.offset.reset", "earliest"); // this one is needed var consumer = kafkaTestUtils.getKafkaConsumer( StringDeserializer.class, StringDeserializer.class, props); // t
  • 63. EMBEDDED KAFKA - PROS you test with the real thing no mocks very close to production
  • 64. EMBEDDED KAFKA - CONS shared cluster between tests → no independent tests in case of an error you deal with a real cluster slow
  • 65. TESTCONTAINERS …a Java library that supports JUnit tests, providing lightweight, throwaway instances of … anything else that can run in a Docker container.
  • 67. TESTCONTAINERS - EXAMPLE TEST @Mock Repository mockRepository; @Container private static final KafkaContainer KAFKA = new KafkaContainer(); @Test public void withTestcontainers() { // arrange var producerProps = new Properties(); producerProps.put("bootstrap.servers", KAFKA.getBootstrapServers()); producerProps.put("key.serializer", StringSerializer.class); producerProps.put("value.serializer", StringSerializer.class); var consumerProps = new Properties(); consumerProps.put("bootstrap.servers", KAFKA.getBootstrapServers()); consumerProps.put("group.id", "mygroup"); consumerProps.put("auto.offset.reset", "earliest"); consumerProps.put("key.deserializer", StringDeserializer.class); consumerProps.put("value.deserializer", StringDeserializer.class); K fk C St i St i ( P )
  • 68. TESTCONTAINERS - PROS easy to use generally good for integration tests (e.g. database) project has currently a lot of traction
  • 69. TESTCONTAINERS - CONS requires Docker as a separate dependency your CI needs to support Docker as well slow
  • 71. RECALL THE TEST PYRAMID many tests on unit level little test on end-to-end level
  • 72. OUR APPROACH majority: Kafka Fake objects combined with an own BufferedMockConsumer a couple of integration tests based on Testcontainers
  • 73. THANK YOU FOR YOUR ATTENTION!
  • 74. IMAGE REFERENCES [tired] https://pixabay.com/photos/winks-sleeping-snooze-sleep-2383407/ [maki] https://pixabay.com/photos/maki-curious-halfaap-peekaboo-3295891/ [boring] https://pixabay.com/photos/cute-animal-pet-tired-yawn-sleep-3258931/ [balled_up_paper] https://unsplash.com/photos/e4Davs7-XnE [detour] https://unsplash.com/photos/dQLgop4tnsc [endless] https://pixabay.com/de/photos/cassiopeia-supernova-cassiopeia-2515913/ [craft] https://pixabay.com/de/photos/hobel-hammer-s%C3%A4ge-rost-n%C3%A4gel-4607203/