web-dev-qa-db-ja.com

Spring Embeddedを使用して@KafkaListenerをテストするKafka

Spring Boot 2.xを使用して開発しているKafkaリスナーの単体テストを作成しようとしています。単体テストなので、Zookeeperのインスタンスである完全なKafkaサーバーを起動したくありません。そこで、Spring Embedded Kafkaを使用することにしました。

リスナーの定義は非常に基本的です。

@Component
public class Listener {
    private final CountDownLatch latch;

    @Autowired
    public Listener(CountDownLatch latch) {
        this.latch = latch;
    }

    @KafkaListener(topics = "sample-topic")
    public void listen(String message) {
        latch.countDown();
    }
}

また、メッセージを受信した後、latchカウンターがゼロに等しいことを確認するテストは非常に簡単です。

@RunWith(SpringRunner.class)
@SpringBootTest
@DirtiesContext
@EmbeddedKafka(topics = { "sample-topic" })
@TestPropertySource(properties = { "spring.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}" })
public class ListenerTest {

    @Autowired
    private KafkaEmbedded embeddedKafka;

    @Autowired
    private CountDownLatch latch;

    private KafkaTemplate<Integer, String> producer;

    @Before
    public void setUp() {
        this.producer = buildKafkaTemplate();
        this.producer.setDefaultTopic("sample-topic");
    }

    private KafkaTemplate<Integer, String> buildKafkaTemplate() {
        Map<String, Object> senderProps = KafkaTestUtils.producerProps(embeddedKafka);
        ProducerFactory<Integer, String> pf = new DefaultKafkaProducerFactory<>(senderProps);
        return new KafkaTemplate<>(pf);
    }

    @Test
    public void listenerShouldConsumeMessages() throws InterruptedException {
        // Given
        producer.sendDefault(1, "Hello world");
        // Then
        assertThat(latch.await(10L, TimeUnit.SECONDS)).isTrue();
    }
}

残念ながら、テストは失敗し、理由を理解できません。 KafkaEmbeddedのインスタンスを使用して、注釈@KafkaListenerでマークされたメソッドをテストすることはできますか?

すべてのコードは私のGitHubリポジトリで共有されます kafka-listener

ありがとうございます。

12
riccardo.cardin

おそらく、コンシューマーにトピック/パーティションが割り当てられる前にメッセージを送信しているでしょう。プロパティを設定...

spring:
  kafka:
    consumer:
      auto-offset-reset: earliest

...デフォルトはlatestです。

これは、コンソールコンシューマで--from-beginningを使用するようなものです。

[〜#〜] edit [〜#〜]

ああ;ブートのプロパティを使用していません。

追加する

props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");

EDIT2

ところで、送信が成功したことをアサートするには、おそらくget(10L, TimeUnit.SECONDS)Future<>)の結果に対してtemplate.send()も実行する必要があります。

EDIT

テストのためだけにオフセットリセットを無効にするには、ブローカーアドレスに対して行ったのと同じことを実行できます。

@Value("${spring.kafka.consumer.auto-offset-reset:latest}")
private String reset;

...

    props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, this.reset);

そして

@TestPropertySource(properties = { "spring.kafka.bootstrap-servers=${spring.embedded.kafka.brokers}",
        "spring.kafka.consumer.auto-offset-reset=earliest"})

ただし、このプロパティはグループが初めて消費するときにのみ適用されることに注意してください。アプリが起動するたびに常に最後から開始するには、起動中に最後までシークする必要があります。

また、enable.auto.commitfalseに設定することをお勧めします。これにより、コンテナは、タイムスケジュールでコンシューマクライアントに依存するのではなく、オフセットのコミットを処理します。

8
Gary Russell