英文:
Creating an annotation for JUnit 4/5 to initialize and inject an object in tests
问题
我正在为 Kafka 开发一个测试库,Kafkaesque。该库允许您使用流畅而优雅的 API 为 Kafka 开发集成测试。目前,我正在为 Spring Kafka 开发版本。
该库需要在每个测试中进行初始化:
@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();
}
与手动初始化 SpringKafkaesque
对象不同,我希望创建一个可以为我执行这些操作的注释。类似于 Spring Kafka 的 @EmbeddedKafka
注释。
@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();
}
}
是否有可能?有任何建议吗?
英文:
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?
答案1
得分: 1
### JUnit 4
一个可能的解决方案是使用反射创建自定义的注解处理。您可以使用 `@Rule` 获取测试方法的名称,例如:
```java
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) {
// 处理异常
}
if (method.isAnnotationPresent(EmbeddedKafka.class)) {
// 在这里初始化您的 SpringKafkaesque 实例
// kafkaesqueInstance = new SpringKafkaesque(broker)
//
}
}
@EmbeddedKafka
@Test
public void testCustomAnnotated() {
// 在这里编写您的测试
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface EmbeddedKafka {
}
}
您需要将此实例存储在类级变量中。对于没有 @EmbeddedKafka
注解的方法,这个变量将是 null
。
JUnit 5
对于 JUnit 5,您可以考虑使用 ParameterResolver
进行参数注入。首先,您需要实现这个接口:
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 {
// 在这里创建一个 SpringKafkaesque 实例并返回
return new SpringKafkaesque();
}
}
接下来,在您的测试类上添加 @ExtendWith(KafkaesqueResolver.class)
注解,并在需要 SpringKafkaesque
实例的测试方法中添加一个参数:
@ExtendWith(KafkaesqueResolver.class)
public class ParamInjectionTest {
@Test
public void testNoParams() {
// 无需注入任何内容
}
@Test
public void testWithParam(SpringKafkaesque instance) {
// 使用您的实例执行操作
}
}
在这种情况下,不需要自定义注解。
<details>
<summary>英文:</summary>
### JUnit 4
One possible solution is to create a custom annotation processing using reflection. You can get the test method name with `@Rule`, so for example:
```java
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
.
JUnit 5
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论