You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

测试Mock KafkaTemplate时send返回的ListenableFuture始终为null

问题分析

测试中kafkaTemplate.send()返回null的核心原因是Mock对象未正确生效,常见场景包括:

  1. 混用@SpringBootTest与@Mock:@SpringBootTest会启动Spring上下文,主类依赖的KafkaTemplate来自Spring容器而非你创建的Mock实例。
  2. @InjectMocks未完成依赖注入:主类的KafkaTemplate字段可能是final或构造函数注入逻辑不匹配,导致Mockito无法注入Mock对象。
  3. 参数匹配不精确:any(ProducerRecord.class)可能因泛型或参数细节未匹配到主类创建的ProducerRecord实例。

修复方案

方案1:单元测试(不启动Spring上下文)

移除@SpringBootTest,仅用Mockito管理依赖注入,避免Spring上下文干扰:

@ExtendWith(MockitoExtension.class)
class QueryServiceProducerTest {
  @InjectMocks private QueryServiceProducer classUnderTest;
  @Mock KafkaTemplate<String, byte[]> template;
  QueryServiceRequest queryServiceRequest;
  QueryServiceSendStatus expectedStatus;

  @BeforeEach
  void setup() {
    int offset = 0;
    expectedStatus =
        QueryServiceSendStatus.builder()
            .offset(0L)
            .partition(0)
            .error(null)
            .topic("topic")
            .timestamp(123L)
            .build();
    queryServiceRequest =
        new QueryServiceRequest(String.valueOf(UUID.randomUUID()), "data", "topic");

    RecordMetadata recordMetadata =
            new RecordMetadata(
                    new TopicPartition(queryServiceRequest.getTopic(), offset), 0L, 0L, 123L, 0L, 0, 0);

    SettableListenableFuture<SendResult<String, byte[]>> future = new SettableListenableFuture<>();
    future.set(new SendResult<>(
        new ProducerRecord<>("topic", 0, queryServiceRequest.getId(), "data".getBytes(), null), 
        recordMetadata));

    // 用更宽松的参数匹配确保命中Mock逻辑
    when(template.send(any())).thenReturn(future);
  }

  @Test
  void testQueryServiceProducerIsOK() throws ExecutionException, InterruptedException {
    QueryServiceSendStatus status =
        classUnderTest.sendMessage(
            queryServiceRequest.getTopic(),
            0,
            queryServiceRequest.getId(),
            queryServiceRequest.getData().getBytes(),
            null);
    Assertions.assertEquals(expectedStatus, status);
  }
}
  • 优势:测试速度快,完全由Mockito控制依赖,避免Spring代理干扰。
  • 注意:@Async注解在此场景下不会生效(无Spring代理),方法会同步执行,不影响Mock逻辑验证。

方案2:集成测试(保留Spring上下文)

使用@MockBean替换@Mock,让Spring上下文使用你的Mock实例:

@SpringBootTest
class QueryServiceProducerTest {
  @Autowired private QueryServiceProducer classUnderTest;
  @MockBean KafkaTemplate<String, byte[]> template;
  QueryServiceRequest queryServiceRequest;
  QueryServiceSendStatus expectedStatus;

  @BeforeEach
  void setup() {
    int offset = 0;
    expectedStatus =
        QueryServiceSendStatus.builder()
            .offset(0L)
            .partition(0)
            .error(null)
            .topic("topic")
            .timestamp(123L)
            .build();
    queryServiceRequest =
        new QueryServiceRequest(String.valueOf(UUID.randomUUID()), "data", "topic");

    RecordMetadata recordMetadata =
            new RecordMetadata(
                    new TopicPartition(queryServiceRequest.getTopic(), offset), 0L, 0L, 123L, 0L, 0, 0);

    SettableListenableFuture<SendResult<String, byte[]>> future = new SettableListenableFuture<>();
    future.set(new SendResult<>(
        new ProducerRecord<>("topic", 0, queryServiceRequest.getId(), "data".getBytes(), null), 
        recordMetadata));

    when(template.send(any(ProducerRecord.class))).thenReturn(future);
  }

  @Test
  void testQueryServiceProducerIsOK() throws ExecutionException, InterruptedException {
    QueryServiceSendStatus status =
        classUnderTest.sendMessage(
            queryServiceRequest.getTopic(),
            0,
            queryServiceRequest.getId(),
            queryServiceRequest.getData().getBytes(),
            null);
    Assertions.assertEquals(expectedStatus, status);
  }
}
  • 优势:保留Spring上下文,@Async注解正常生效,适合需要验证完整Spring逻辑的集成测试。

额外检查项

  1. 主类的KafkaTemplate字段不能是final(构造函数注入除外),否则Mockito无法完成注入。
  2. 确保导入正确的Mockito类:org.mockito.Mock、org.mockito.junit.jupiter.MockitoExtension、org.mockito.Mockito.when。
  3. 如果主类用构造函数注入KafkaTemplate,需确保构造函数参数与@Mock实例类型完全匹配。

内容的提问来源于stack exchange,提问作者Beez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 08:27:13