GH-666: Single global embedded Kafka with JUnit 5 (#2308)

* GH-666: Single global embedded Kafka with JUnit 5

Fixes https://github.com/spring-projects/spring-kafka/issues/666

* Introduce a `GlobalEmbeddedKafkaTestExecutionListener` which is registered
by the service loader from JUnit Platform
* Make its activation conditional based on the `spring.kafka.global.embedded.enabled`
system property
* Expose some other configuration properties for the target global `EmbeddedKafkaBroker`
* Verify its functionality via manual `Launcher.execute()`
* Add more `@DirtiesContext` to some tests in the `spring-kafka-test` which don't close
their embedded brokers on the exit

* * Print local launcher summary into SOUT for tracing the nested problems

* * Fix prefix for `file:` resource in the test

* * Make `junit-platform-launcher` as provided dep -
this is part of JUnit platform the test plan is going to be run
* Add missed new line in the end of `junit-platform.properties`
* Rethrow an exception from the local launcher in the `GlobalEmbeddedKafkaTestExecutionListenerTests`
to fail if its suite test classes have failed.

* * Fix JavaDocs for `GlobalEmbeddedKafkaTestExecutionListener` constants
* Move system properties clean up in the `GlobalEmbeddedKafkaTestExecutionListenerTests` into `@AfterAll`
* Add docs for this new feature
* Add `sample-05` to demonstrate global embedded Kafka in action via Maven configuration

* * Mention `sample-05` in the common `README.adoc` for samples

* * Fix language in the `testing.adoc`
* Add new lines into new files
* Add `spring.kafka.consumer.auto-offset-reset=earliest` to make a consumer to start from the beginning of the partition:
fixes a race condition when publishing happens before consumer is started
* Make `mvnw` as an executable
* Add `spring.embedded.kafka.brokers.property` explanation into docs

* * Fix language in docs

Co-authored-by: Gary Russell <grussell@vmware.com>

* * Make `Sample05Application2Tests` passing
* Remove `deliberately failing` sentence from the JavaDoc an README
* Clean up unused imports

Co-authored-by: Gary Russell <grussell@vmware.com>
This commit is contained in:
Artem Bilan
2022-06-22 17:14:14 -04:00
committed by GitHub
parent 596f7d940c
commit 0f69553736
24 changed files with 1212 additions and 5 deletions

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.kafka.annotation.KafkaListener;
/**
* Plain Kafka listener sample which purpose is only to be used in the tests assertions.
* See unit tests for this project for more information.
*
* @author Artem Bilan
*
* @since 3.0
*
*/
@SpringBootApplication
public class Sample05Application {
public static void main(String[] args) {
SpringApplication.run(Sample05Application.class, args);
}
@KafkaListener(id ="sampleListener", topics = "topic1")
void listenForTopic(String payload) {
System.out.println("Received: " + payload);
}
}

View File

@@ -0,0 +1,4 @@
spring.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}
spring.kafka.producer.properties[max.block.ms]=1000
spring.kafka.consumer.auto-offset-reset=earliest
logging.level.root=error

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example;
import static org.awaitility.Awaitility.await;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.test.annotation.DirtiesContext;
/**
* This test is going to fail from IDE since there is no exposed {@code spring.embedded.kafka.brokers} system property.
* Use Maven to run tests which enables global embedded Kafka broker via properties provided to Surefire plugin.
*/
@ExtendWith(OutputCaptureExtension.class)
@SpringBootTest
@DirtiesContext
class Sample05Application1Tests {
@Autowired
KafkaTemplate<String, String> kafkaTemplate;
@Test
void testKafkaListener(CapturedOutput output) {
this.kafkaTemplate.send("topic1", "testData");
await().until(() -> output.getOut().contains("Received: testData"));
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.util.concurrent.TimeUnit;
import org.apache.kafka.common.errors.TimeoutException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.kafka.KafkaException;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.test.annotation.DirtiesContext;
/**
* This test is going to fail from IDE since there is no exposed {@code spring.embedded.kafka.brokers} system property.
* This test demonstrates that global embedded Kafka broker config for {@code auto.create.topics.enable=false}
* is in an effect - the topic {@code nonExistingTopic} does not exist on the broker.
* See {@code /resources/kafka-broker.properties} and Maven Surefire plugin configuration.
*/
@SpringBootTest
@DirtiesContext
class Sample05Application2Tests {
@Autowired
KafkaTemplate<String, String> kafkaTemplate;
@Test
void testKafkaTemplateSend() {
assertThatExceptionOfType(KafkaException.class)
.isThrownBy(() ->
this.kafkaTemplate.send("nonExistingTopic", "fake data").get(10, TimeUnit.SECONDS))
.withRootCauseExactlyInstanceOf(TimeoutException.class)
.withMessageContaining("Send failed")
.withStackTraceContaining("Topic nonExistingTopic not present in metadata");
}
}

View File

@@ -0,0 +1 @@
spring.kafka.embedded.topics=topic1,topic2

View File

@@ -0,0 +1 @@
auto.create.topics.enable=false

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.kafka.test.junit" level="debug"/>
<logger name="org.springframework.kafka.test" level="warn"/>
<logger name="org.apache.kafka.clients" level="warn"/>
<logger name="org.apache.kafka.clients.NetworkClient" level="error"/>
<logger name="org.apache.kafka.common.network.Selector" level="error"/>
<logger name="kafka.server.ReplicaFetcherThread" level="error"/>
<logger name="kafka.server.LogDirFailureChannel" level="error"/>
<logger name="kafka.server.BrokerMetadataCheckpoint" level="error"/>
<logger name="kafka.utils.CoreUtils$" level="error"/>
<logger name="org.apache.zookeeper.server.ZooKeeperServer" level="error"/>
<root level="error">
<appender-ref ref="STDOUT" />
</root>
</configuration>