-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
It seems like there is no convenient way to define single shared KafkaEmbedded that will be used in all JUnit tests.
There might be multiple tests that require KafkaEmbedded
.
When @KafkaEmbedded
is used with @ClassRule
, Kafka server is started and stopped for every test class.
It may take a lot of time. Better to have embedded Kafka server started only once for all test classes.
Straightforward approach is to add to test sources configuration class defining a KafkaEmbedded
bean:
@Configuration
public class KafkaEmbeddedConfig {
@Bean
public KafkaEmbedded kafkaEmbedded() {
KafkaEmbedded kafkaEmbedded = new KafkaEmbedded(1, false, 1, "test_topic");
kafkaEmbedded.setKafkaPorts(9092);
return kafkaEmbedded;
}
}
The problem is that there might tests creating multiple test contexts:
@TestPropertySource("classpath:test.properties")
@RunWith(SpringRunner.class)
@SpringBootTest
public class KafkaListenerTest {
...
and
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class RestControllerTest {
...
In this example Kafka server will be started twice. Second executed test class will fail with the exception
Caused by: kafka.common.KafkaException: Socket server failed to bind to localhost:9092: Address already in use.
if port is specified or new Kafka server will be started if random port is used.
Moreover, if @DirtiesContext
is used, together with the refresh of the test context Kafka server will be restarted.
As a workaround KafkaEmbedded
can be defined as a constant in some base test class that test classes will extend:
@ActiveProfiles("kafka")
public class KafkaTestBase {
public static final KafkaEmbedded KAFKA_EMBEDDED = createKafkaEmbedded();
private static KafkaEmbedded createKafkaEmbedded() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(KafkaEmbeddedConfig.class);
KafkaEmbedded kafkaEmbedded = context.getBean(KafkaEmbedded.class);
Runtime.getRuntime().addShutdownHook(new Thread(context::close));
return kafkaEmbedded;
}
}
@TestPropertySource("classpath:test.properties")
@RunWith(SpringRunner.class)
@SpringBootTest
public class KafkaListenerTest extends KafkaTestBase {
...
This way Kafka server will be started in a separate Spring application context that will not be affected by @DirtiesContext
or multiple test contexts (due to the usage of @AutoConfigureMockMvc
etc.).
Can such approach be considered a normal solution or a workaround?
Are there any more efficient ways to have single shared KafkaEmbedded
instance across all JUnit tests?
Sample:
- Repo with sample project reproducing the problem with exception
BindException: Address already in use
: https://github.com/evgeniy-khist/spring-kafka-embedded-sample - Fix making
KafkaEmbedded
a constant of base test class:
https://github.com/evgeniy-khist/spring-kafka-embedded-sample/pull/1/files