【问题标题】:Creating an annotation for JUnit 4/5 to initialize and inject an object in tests为 JUnit 4/5 创建注释以在测试中初始化和注入对象
【发布时间】:2021-01-06 04:06:11
【问题描述】:

我正在为 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();
   }

有可能吗?有什么建议吗?

【问题讨论】:

  • 我想知道如果在测试配置中使用@KafkaListener 有一种简单的方法,为什么要这样做......
  • 嗨,@ArtemBilan。感谢您的评论。您为 Spring Kafka 提供的库的问题在于它是完全异步的。每次有人想从某个主题消费某些东西时,他必须编写代码来等待该主题中的消息可用,然后将它们放入队列中,等等。或者,至少,这是我对 Spring Kafka 的体验。如果您有更好的想法,请告诉我:)

标签: java junit apache-kafka annotations spring-kafka


【解决方案1】:

JUnit 4

一种可能的解决方案是使用反射创建自定义注释处理。可以通过@Rule获取测试方法名,例如:

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 {
    }
}

您需要将此实例存储在类级变量中。对于没有@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 {
        // Create an instance of SpringKafkaesque here and return it
        return new SpringKafkaesque();
    }
}

接下来,在你的测试类中添加@ExtendWith(KafkaesqueResolver.class)注解,并在你的测试方法中添加一个参数,这里需要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
    }
}

在这种情况下不需要自定义注释。

【讨论】:

  • 非常感谢。但是,如何将在init() 方法中创建的Kafkaesque 实例与名为testCustomAnnotated 的测试共享?
  • @riccardo.cardin 为此声明一个类级变量并将实例分配给它
  • @riccardo.cardin,我用 JUnit 5 解决方案更新了我的答案
猜你喜欢
  • 2021-04-09
  • 1970-01-01
  • 1970-01-01
  • 2017-12-23
  • 1970-01-01
  • 1970-01-01
  • 2010-11-27
  • 2011-02-24
相关资源
最近更新 更多