Java 对卡夫卡拓扑进行单元测试,该拓扑为';s使用kstream连接

Java 对卡夫卡拓扑进行单元测试,该拓扑为';s使用kstream连接,java,unit-testing,join,apache-kafka,apache-kafka-streams,Java,Unit Testing,Join,Apache Kafka,Apache Kafka Streams,我有一个拓扑结构可以进行两个kstream连接,我面临的问题是,当TopologyTestDriver通过pipeInput和readOutput发送两条消费记录进行单元测试时。它似乎不起作用 我想这可能是因为连接在实际的卡夫卡中使用了内部rocksdb,而我们在测试中没有使用 因此,我一直在寻找解决方案,但没有找到任何 注意:当删除kstream kstream连接时,此测试方法工作得非常好 我有一个拓扑结构可以进行两个kstream连接,我面临的问题是,当TopologyTestDriver

我有一个拓扑结构可以进行两个kstream连接,我面临的问题是,当TopologyTestDriver通过pipeInput和readOutput发送两条消费记录进行单元测试时。它似乎不起作用

我想这可能是因为连接在实际的卡夫卡中使用了内部rocksdb,而我们在测试中没有使用

因此,我一直在寻找解决方案,但没有找到任何

注意:当删除kstream kstream连接时,此测试方法工作得非常好

我有一个拓扑结构可以进行两个kstream连接,我面临的问题是,当TopologyTestDriver通过pipeInput和readOutput发送两条消费记录进行单元测试时。它似乎不起作用

根据设计,但不幸的是在您的案例中,
TopologyTestDriver
并不是Kafka Streams引擎在运行时如何工作的100%精确模型。值得注意的是,新传入事件的处理顺序存在一些差异

例如,在尝试测试某些联接时,这确实会导致问题,因为这些操作依赖于特定的处理顺序(例如,在流表联接中,在'alice'的流侧事件到达之前,表应该已经有了键'alice'的条目,否则流侧'alice'的联接输出将不包括任何表侧数据)

因此,我一直在寻找解决方案,但没有找到任何

我的建议是使用启动嵌入式Kafka集群的测试,然后使用“真正的”Kafka Streams引擎(即,不是
TopologyTestDriver
)对该集群运行测试。实际上,这意味着您正在将您的测试从单元测试更改为集成/系统测试:您的测试将启动一个完整的Kafka Streams拓扑,该拓扑与在与您的测试相同的机器上运行的嵌入式Kafka群集通信

请参阅ApacheKafka项目中Kafka流的集成测试,其中和是该工具的中心部分。联接的一个具体测试示例是(有一些联接相关的集成测试)及其父级。(值得一提的是,在上还有更多的集成测试示例,其中包括在使用ApacheAvro作为数据格式时还包括Confluent Schema Registry的测试,等等)


但是,除非我弄错了,否则集成测试及其工具不包括在Kafka Streams的工件中(即,
org.apache.Kafka:Kafka Streams test utils
)。因此您必须将一些复制粘贴到您自己的代码库中。

您看过Kafka Streams单元测试吗[1]?它是关于数据管道和用模拟处理器检查最终结果

例如,对于以下流连接:

        stream1 = builder.stream(topic1, consumed);
        stream2 = builder.stream(topic2, consumed);
        joined = stream1.outerJoin(
            stream2,
            MockValueJoiner.TOSTRING_JOINER,
            JoinWindows.of(ofMillis(100)),
            StreamJoined.with(Serdes.Integer(), Serdes.String(), Serdes.String()));
        joined.process(supplier);
然后,您可以开始将输入项管道化到第一个或第二个主题中,并在每个连续的输入管道中检查处理器可以检查的内容:

// push two items to the primary stream; the other window is empty
            // w1 = {}
            // w2 = {}
            // --> w1 = { 0:A0, 1:A1 }
            //     w2 = {}
            for (int i = 0; i < 2; i++) {
                inputTopic1.pipeInput(expectedKeys[i], "A" + expectedKeys[i]);
            }
            processor.checkAndClearProcessResult(EMPTY);

            // push two items to the other stream; this should produce two items
            // w1 = { 0:A0, 1:A1 }
            // w2 = {}
            // --> w1 = { 0:A0, 1:A1 }
            //     w2 = { 0:a0, 1:a1 }
            for (int i = 0; i < 2; i++) {
                inputTopic2.pipeInput(expectedKeys[i], "a" + expectedKeys[i]);
            }
            processor.checkAndClearProcessResult(new KeyValueTimestamp<>(0, "A0+a0", 0),
                new KeyValueTimestamp<>(1, "A1+a1", 0));
//将两个项目推送到主流;另一个窗口为空
//w1={}
//w2={}
//-->w1={0:A0,1:A1}
//w2={}
对于(int i=0;i<2;i++){
InputOpic1.pipeInput(预期键[i],“A”+预期键[i]);
}
processor.checkAndClearProcessResult(空);
//将两个项目推送到另一个流;这将产生两个项目
//w1={0:A0,1:A1}
//w2={}
//-->w1={0:A0,1:A1}
//w2={0:a0,1:a1}
对于(int i=0;i<2;i++){
InputOpic2.pipeInput(预期键[i],“a”+预期键[i]);
}
processor.checkAndClearProcessResult(新的KeyValueTimestamp(0,“A0+A0”,0),
新的KeyValueTimestamp(1,“A1+A1”,0);
我希望这有帮助

参考资料: [1]