I am developing a testing library for Kafka, Kafkaesque . The library lets you develop integration tests for Kafka using a fluid and elegant (?!) API. For now, I develop the version for Spring Kafka.
The library needs to be initialized in every test:
@Test
void consumeShouldConsumeMessagesProducesFromOutsideProducer() {
kafkaTemplate.sendDefault(1, "data1");
kafkaTemplate.sendDefault(2, "data2");
new SpringKafkaesque(broker)
.<Integer, String>consume()
.fromTopic(CONSUMER_TEST_TOPIC)
.waitingAtMost(1L, TimeUnit.SECONDS)
.waitingEmptyPolls(5, 100L, TimeUnit.MILLISECONDS)
.withDeserializers(new IntegerDeserializer(), new StringDeserializer())
.expecting()
.havingRecordsSize(2)
.assertingThatPayloads(Matchers.containsInAnyOrder("data1", "data2"))
.andCloseConsumer();
}
Instead of manually initializing the SpringKafkaesque
object, I want to create an annotation that does the magic for me. Something like the @EmbeddedKafka
annotation of Spring Kafka.
@SpringBootTest(classes = {TestConfiguration.class})
@Kafkaesque(
topics = {SpringKafkaesqueTest.CONSUMER_TEST_TOPIC, SpringKafkaesqueTest.PRODUCER_TEST_TOPIC})
class SpringKafkaesqueTest {
@Autowired
private Kafkaesque kafkaesque;
@Test
void consumeShouldConsumeMessagesProducesFromOutsideProducer() {
kafkaTemplate.sendDefault(1, "data1");
kafkaTemplate.sendDefault(2, "data2");
kafkaesque
.<Integer, String>consume()
.fromTopic(CONSUMER_TEST_TOPIC)
.waitingAtMost(1L, TimeUnit.SECONDS)
.waitingEmptyPolls(5, 100L, TimeUnit.MILLISECONDS)
.withDeserializers(new IntegerDeserializer(), new StringDeserializer())
.expecting()
.havingRecordsSize(2)
.assertingThatPayloads(Matchers.containsInAnyOrder("data1", "data2"))
.andCloseConsumer();
}
Is it possible? Any suggestion?
One possible solution is to create a custom annotation processing using reflection. You can get the test method name with @Rule
, so for example:
public class CustomAnnotationTest {
private SpringKafkaesque kafkaesqueInstance;
@Rule
public TestName testName = new TestName();
@Before
public void init() {
Method method = null;
try {
method = this.getClass().getMethod(testName.getMethodName());
} catch (Exception ex) {
// handle exceptions
}
if (method.isAnnotationPresent(EmbeddedKafka.class)) {
// Init your SpringKafkaesque instance here
// kafkaesqueInstance = new SpringKafkaesque(broker)
//
}
}
@EmbeddedKafka
@Test
public void testCustomAnnotated() {
// your test here
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface EmbeddedKafka {
}
}
You need to store this instance in the class-level variable. For the methods with no @EmbeddedKafka
annotation, this variable will be null
.
With JUnit 5 you may consider using parameter injection with ParameterResolver
. First of all, you need to implement this interface:
public class KafkaesqueResolver implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
return parameterContext.getParameter().getType() == SpringKafkaesque.class;
}
@Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
// Create an instance of SpringKafkaesque here and return it
return new SpringKafkaesque();
}
}
Next, add @ExtendWith(KafkaesqueResolver.class)
annotation to your test class, and add a parameter to your test method, where you need the instance of SpringKafkaesque
:
@ExtendWith(KafkaesqueResolver.class)
public class ParamInjectionTest {
@Test
public void testNoParams() {
// nothing to inject
}
@Test
public void testWithParam(SpringKafkaesque instance) {
// do what you need with your instance
}
}
No custom annotation required in this case.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.