spring-kafka-test大揭秘

​已经到了没有测试案例,就不会写代码的年纪了!不过好在,优秀的框架或组件总是会在介绍完主体功能之后,附带介绍如何进行测试。

然后,因为工作原因,又简单研究了下kafka的测试框架。其中,最神奇的地方是,Spring团队为了便于测试,秉持着能内嵌一定内嵌的原则,搞了一套内嵌的zookeeper和kafka。这样在单元测试期间,就不用开发人员再准备相关环境了。

那么,下面来揭秘下具体是如何实现的:

1. 依赖

<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka-test</artifactId>
    <scope>test</scope>
</dependency>

还是基于Spring框架来研究,我们现在离开了春天框架,就感觉一夜回到解放前,啥都要自己从零开始搭建了。

2. 测试类

首先,看下测试类的定义部分:

@RunWith(SpringRunner.class)
@EmbeddedKafka(topics = "cat")
@ImportAutoConfiguration(KafkaAutoConfiguration.class)
public class SpringBootKafkaTest

很简单的定义,主要包括两部分:

  • @EmbeddedKafka(topics = "cat"):用来定义内嵌的kafka broker,topics参数用于指定需要创建的topic;

  • ImportAutoConfiguration:一般测试的时候,我都比较排斥直接使用@SpringBootTest注解,这玩意儿太重了,把能加载的都加载了,搞得巨慢无比。这里面我们只要加载和kafka相关的部分就行了,也就是加载KafkaAutoConfiguration就够了;KafkaAutoConfiguration这个类在上篇介绍过,这里直接忽略;

注意!!!KafkaAutoConfiguration类初始化kafka框架时,是通过参数spring.kafka.bootstrap-servers确定要连接的kafka broker列表的。如果没有这个参数,则默认使用:localhost:9092

上面这点漏掉了,补充一下。如果没有这个信息,后面整个框架都串不起来了。然后spring.kafka.bootstrap-servers默认值是通过这段设置的,代码贴一下,证明不是我胡说:

再来看一段奇丑无比的静态方法段代码:

public class SpringBootKafkaTest {
    static {
        System.setProperty(EmbeddedKafkaBroker.BROKER_LIST_PROPERTY, "spring.kafka.bootstrap-servers");
    }

设置了一个环境变量,用BROKER_LIST_PROPERTY常量作为key,保存了spring.kafka.bootstrap-servers这个值。这个spring.kafka.bootstrap-servers刚刚说过,保存的是kafka broker的地址列表。

这段代码确实丑!这也不是我说的,因为在后面更高的版本中,这种写法已经被官方自己改掉了。

3. 揭秘开始

好了,现在开始揭秘!

首先,研究下包结构:

看到了一个spring.factories文件,一定不能放过,看下内容,一行信息:

# Spring Test ContextCustomizerFactories
org.springframework.test.context.ContextCustomizerFactory=\
org.springframework.kafka.test.context.EmbeddedKafkaContextCustomizerFactory

所以,逻辑可能在EmbeddedKafkaContextCustomizerFactory这里面:

EmbeddedKafkaContextCustomizerFactory这个类创建了一个EmbeddedKafkaContextCustomizer对象,并且将@EmbeddedKafka注解的信息传给了它。

代码较短,贴一下:

 @Override
 public ContextCustomizer createContextCustomizer(Class<?> testClass,
   List<ContextConfigurationAttributes> configAttributes) {
  EmbeddedKafka embeddedKafka =
    AnnotatedElementUtils.findMergedAnnotation(testClass, EmbeddedKafka.class);
  return embeddedKafka != null ? new EmbeddedKafkaContextCustomizer(embeddedKafka) : null;
 }

3.1 EmbeddedKafkaContextCustomizer

这个EmbeddedKafkaContextCustomizer就是核心逻辑所在的位置了,其中有个customizeContext方法。

customizeContext创建了一个EmbeddedKafkaBroker对象,并将这个对象交给了Spring的IoC容器管理:

// EmbeddedKafkaContextCustomizer.customizeContext中的代码片段
EmbeddedKafkaBroker embeddedKafkaBroker = new EmbeddedKafkaBroker(this.embeddedKafka.count(),
  this.embeddedKafka.controlledShutdown(),
  this.embeddedKafka.partitions(),
  topics);
// EmbeddedKafkaContextCustomizer.customizeContext中的代码片段
beanFactory.initializeBean(embeddedKafkaBroker, EmbeddedKafkaBroker.BEAN_NAME);

这个initializeBean方法会触发bean对象的afterPropertiesSet方法执行。然后呢,在EmbeddedKafkaBroker中,刚好有一个,真是凑巧^.^。

3.2 EmbeddedKafkaBroker

EmbeddedKafkaBroker.afterPropertiesSet方法主要完成了zookeeper和kafka broker的创建工作:

// EmbeddedKafkaBroker.afterPropertiesSet方法的代码片段
this.zookeeper = new EmbeddedZookeeper();
...
// EmbeddedKafkaBroker.afterPropertiesSet方法的代码片段
KafkaServer server = TestUtils.createServer(new KafkaConfig(brokerConfigProperties), Time.SYSTEM);
...
// EmbeddedKafkaBroker.afterPropertiesSet方法的代码片段
createKafkaTopics(this.topics);

在这个方法的最后,有一段代码:

this.brokerListProperty = System.getProperty(BROKER_LIST_PROPERTY);
if (this.brokerListProperty == null) {
 this.brokerListProperty = SPRING_EMBEDDED_KAFKA_BROKERS;
}
System.setProperty(this.brokerListProperty, getBrokersAsString());

这个BROKER_LIST_PROPERTY的值,前面已经介绍过了,就是spring.kafka.bootstrap-servers。所以在方法的最后,将kafka broker的地址列表,又写到名称为spring.kafka.bootstrap-servers的环境变量中。

然后,上面所有的这些方法的执行,都在KafkaAutoConfiguration相关方法执行之前完成执行的。

所以,在KafkaAutoConfiguration对kafka框架初始化时,就可以通过spring.kafka.bootstrap-servers参数,连接到内嵌的kafka集群上来了。完美地衔接上了!

好了,揭秘结束!

 

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

镜悬xhs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值