Spring boot EmbeddedKafka AdminClient在Spring应用程序启动测试之前关闭
我正在尝试为SpringKafka应用程序编写集成测试(SpringBoot2.0.6,SpringKafka 2.1.10)我看到了很多Spring boot EmbeddedKafka AdminClient在Spring应用程序启动测试之前关闭,spring-boot,spring-test,spring-kafka,spring-boot-test,Spring Boot,Spring Test,Spring Kafka,Spring Boot Test,我正在尝试为SpringKafka应用程序编写集成测试(SpringBoot2.0.6,SpringKafka 2.1.10)我看到了很多INFO org.apache.zookeeper.server.prerequestprocessor的实例-在处理sessionid:0x166e432ebec0001类型:create cxid:0x5e zxid:0x24 txntype:-1请求路径:n/a错误路径:/brokers/topics/my topic/partitions错误:keep
INFO org.apache.zookeeper.server.prerequestprocessor的实例-在处理sessionid:0x166e432ebec0001类型:create cxid:0x5e zxid:0x24 txntype:-1请求路径:n/a错误路径:/brokers/topics/my topic/partitions错误:keeperrorcode=NoNode for/代理/主题/我的主题/分区
,以及Spring应用程序启动前日志中显示的各种类型的路径(/brokers
,/brokers/topics
,等等)。AdminClient随后关闭,并记录此消息:
DEBUG org.apache.kafka.common.network.Selector - [SocketServer brokerId=0] Connection with /127.0.0.1 disconnected
java.io.EOFException: null
at org.apache.kafka.common.network.NetworkReceive.readFromReadableChannel(NetworkReceive.java:124)
at org.apache.kafka.common.network.NetworkReceive.readFrom(NetworkReceive.java:93)
at org.apache.kafka.common.network.KafkaChannel.receive(KafkaChannel.java:235)
at org.apache.kafka.common.network.KafkaChannel.read(KafkaChannel.java:196)
at org.apache.kafka.common.network.Selector.attemptRead(Selector.java:547)
at org.apache.kafka.common.network.Selector.pollSelectionKeys(Selector.java:483)
at org.apache.kafka.common.network.Selector.poll(Selector.java:412)
at kafka.network.Processor.poll(SocketServer.scala:575)
at kafka.network.Processor.run(SocketServer.scala:492)
at java.lang.Thread.run(Thread.java:748)
我在测试中使用@ClassRule启动选项,如下所示:
@ClassRule
@Shared
private KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, true, 'my-topic')
,自动连接卡夫卡模板,并根据嵌入的卡夫卡值设置连接的弹簧属性:
def setupSpec() {
System.setProperty('spring.kafka.bootstrap-servers', embeddedKafka.getBrokersAsString());
System.setProperty('spring.cloud.stream.kafka.binder.zkNodes', embeddedKafka.getZookeeperConnectionString());
}
一旦Spring应用程序启动,我可以再次看到用户级KeeperException消息的实例:o.a.z.server.PrerequestProcessor:Get user level KeeperException when processing sessionid:0x166e445836d0001 type:setData cxid:0x6b zxid:0x2b txntype:-1请求路径:n/a错误路径:/config/topics/\u consumer\u偏移错误:KeeperErrorCode=Noode for/配置/topics/\u消费者\u偏移量
知道我哪里出了问题吗?我可以提供其他设置信息和日志消息,但只是对最初可能最有用的内容进行了有根据的猜测。我不熟悉Spock,但我知道
@KafkaListener
方法是在其自己的线程上调用的,因此您不能直接在然后:
块中断言它
您需要在测试用例中以某种方式确保阻塞等待
我尝试使用BlockingVariable
来对抗真正的服务notmock,我在日志中看到了您的println(message)
。但是BlockingVariable
在某种程度上对我来说仍然不起作用:
@DirtiesContext
@SpringBootTest(classes = [KafkaIntTestApplication.class])
@ActiveProfiles('test')
class CustomListenerSpec extends Specification {
@ClassRule
@Shared
public KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, false, 'my-topic')
@Autowired
private KafkaTemplate<String, String> template
@SpyBean
private SimpleService service
final def TOPIC_NAME = 'my-topic'
def setupSpec() {
System.setProperty('spring.kafka.bootstrapServers', embeddedKafka.getBrokersAsString());
}
def 'Sample test'() {
given:
def testMessagePayload = "Test message"
def message = MessageBuilder.withPayload(testMessagePayload).setHeader(KafkaHeaders.TOPIC, TOPIC_NAME).build()
def result = new BlockingVariable<Boolean>(5)
service.handleMessage(_) >> {
result.set(true)
}
when: 'We put a message on the topic'
template.send(message)
then: 'the service should be called'
result.get()
}
}
我还必须添加此依赖项:
testImplementation "org.hamcrest:hamcrest-core"
更新
嗯。真正的问题是,
MockConfig
对于测试上下文配置不可见,而@Import(MockConfig.class)
起到了作用。其中,@Primary
还为我们提供了额外的信号,告诉我们在测试类中选择什么bean进行注入。@ArtemBilan的回答让我走上了正确的道路,因此感谢他插话,在查看其他BlockingVariable
文章和示例后,我能够找到答案。我在模拟的响应中使用了BlockingVariable
,而不是作为回调。调用mock的响应时,使其将值设置为true,然后块执行result.get()
并通过测试
@DirtiesContext
@ActiveProfiles('test')
@SpringBootTest
@Import(MockConfig.class)
class CustomListenerSpec extends TestSpecBase {
@ClassRule
@Shared
private KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, false, TOPIC_NAME)
@Autowired
private KafkaTemplate<String, String> template
@Autowired
private SimpleService service
final def TOPIC_NAME = 'my-topic'
def setupSpec() {
System.setProperty('spring.kafka.bootstrap-servers', embeddedKafka.getBrokersAsString());
}
def 'Sample test'() {
def testMessagePayload = "Test message"
def message = MessageBuilder.withPayload(testMessagePayload).setHeader(KafkaHeaders.TOPIC, TOPIC_NAME).build()
def result = new BlockingVariable<Boolean>(5)
service.handleMessage(_ as String) >> {
result.set(true)
}
when: 'We put a message on the topic'
template.send(message)
then: 'the service should be called'
result.get()
}
}
@DirtiesContext
@ActiveProfiles(“测试”)
@春靴测试
@导入(MockConfig.class)
类CustomListenerSpec扩展了TestSpecBase{
@阶级规则
@共享
private KafkameBedded embeddedKafka=新KafkameBedded(1,false,主题名称)
@自动连线
私有KafkaTemplate模板
@自动连线
私有SimpleService服务
final def TOPIC_NAME='我的主题'
def setupSpec(){
System.setProperty('spring.kafka.bootstrap servers',embeddedKafka.getBrokersAsString());
}
def“样本测试”(){
def testMessagePayload=“测试消息”
def message=MessageBuilder.withPayload(testMessagePayload).setHeader(KafkaHeaders.TOPIC,TOPIC_NAME).build()
def结果=新阻塞变量(5)
service.handleMessage(u作为字符串)>>{
result.set(真)
}
当:'我们在主题上留言'
模板发送(消息)
然后:“应该调用服务”
result.get()
}
}
也许您可以与我们分享一个简单的GitHub项目,以便播放和复制?Thanks@ArtemBilan下面是一个示例项目,感谢您查看:
@DirtiesContext
@ActiveProfiles('test')
@SpringBootTest
@Import(MockConfig.class)
class CustomListenerSpec extends TestSpecBase {
@ClassRule
@Shared
private KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, false, TOPIC_NAME)
@Autowired
private KafkaTemplate<String, String> template
@Autowired
private SimpleService service
final def TOPIC_NAME = 'my-topic'
def setupSpec() {
System.setProperty('spring.kafka.bootstrap-servers', embeddedKafka.getBrokersAsString());
}
def 'Sample test'() {
def testMessagePayload = "Test message"
def message = MessageBuilder.withPayload(testMessagePayload).setHeader(KafkaHeaders.TOPIC, TOPIC_NAME).build()
def result = new BlockingVariable<Boolean>(5)
service.handleMessage(_ as String) >> {
result.set(true)
}
when: 'We put a message on the topic'
template.send(message)
then: 'the service should be called'
result.get()
}
}