org.apache.kafka.streams.TestOutputTopic Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kafka.streams;
import org.apache.kafka.common.serialization.Deserializer;
import org.apache.kafka.streams.test.TestRecord;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
/**
* {@code TestOutputTopic} is used to read records from a topic in {@link TopologyTestDriver}.
* To use {@code TestOutputTopic} create a new instance via
* {@link TopologyTestDriver#createOutputTopic(String, Deserializer, Deserializer)}.
* In actual test code, you can read record values, keys, {@link KeyValue} or {@link TestRecord}
* If you have multiple source topics, you need to create a {@code TestOutputTopic} for each.
*
* If you need to test key, value and headers, use {@link #readRecord()} methods.
* Using {@link #readKeyValue()} you get a {@link KeyValue} pair, and thus, don't get access to the record's
* timestamp or headers.
* Similarly using {@link #readValue()} you only get the value of a record.
*
*
Processing records
* {@code
* private TestOutputTopic outputTopic;
* ...
* outputTopic = testDriver.createOutputTopic(OUTPUT_TOPIC, stringDeserializer, longDeserializer);
* ...
* assertThat(outputTopic.readValue()).isEqual(1);
* }
*
* @param the type of the record key
* @param the type of the record value
* @see TopologyTestDriver
*/
public class TestOutputTopic {
private final TopologyTestDriver driver;
private final String topic;
private final Deserializer keyDeserializer;
private final Deserializer valueDeserializer;
TestOutputTopic(final TopologyTestDriver driver,
final String topicName,
final Deserializer keyDeserializer,
final Deserializer valueDeserializer) {
Objects.requireNonNull(driver, "TopologyTestDriver cannot be null");
Objects.requireNonNull(topicName, "topicName cannot be null");
Objects.requireNonNull(keyDeserializer, "keyDeserializer cannot be null");
Objects.requireNonNull(valueDeserializer, "valueDeserializer cannot be null");
this.driver = driver;
this.topic = topicName;
this.keyDeserializer = keyDeserializer;
this.valueDeserializer = valueDeserializer;
}
/**
* Read one record from the output topic and return record's value.
*
* @return Next value for output topic.
*/
public V readValue() {
final TestRecord record = readRecord();
return record.value();
}
/**
* Read one record from the output topic and return its key and value as pair.
*
* @return Next output as {@link KeyValue}.
*/
public KeyValue readKeyValue() {
final TestRecord record = readRecord();
return new KeyValue<>(record.key(), record.value());
}
/**
* Read one Record from output topic.
*
* @return Next output as {@link TestRecord}.
*/
public TestRecord readRecord() {
return driver.readRecord(topic, keyDeserializer, valueDeserializer);
}
/**
* Read output to List.
* This method can be used if the result is considered a stream.
* If the result is considered a table, the list will contain all updated, ie, a key might be contained multiple times.
* If you are only interested in the last table update (ie, the final table state),
* you can use {@link #readKeyValuesToMap()} instead.
*
* @return List of output.
*/
public List> readRecordsToList() {
final List> output = new LinkedList<>();
while (!isEmpty()) {
output.add(readRecord());
}
return output;
}
/**
* Read output to map.
* This method can be used if the result is considered a table,
* when you are only interested in the last table update (ie, the final table state).
* If the result is considered a stream, you can use {@link #readRecordsToList()} instead.
* The list will contain all updated, ie, a key might be contained multiple times.
* If the last update to a key is a delete/tombstone, the key will still be in the map (with null-value).
*
* @return Map of output by key.
*/
public Map readKeyValuesToMap() {
final Map output = new HashMap<>();
TestRecord outputRow;
while (!isEmpty()) {
outputRow = readRecord();
if (outputRow.key() == null) {
throw new IllegalStateException("Null keys not allowed with readKeyValuesToMap method");
}
output.put(outputRow.key(), outputRow.value());
}
return output;
}
/**
* Read all KeyValues from topic to List.
*
* @return List of output KeyValues.
*/
public List> readKeyValuesToList() {
final List> output = new LinkedList<>();
KeyValue outputRow;
while (!isEmpty()) {
outputRow = readKeyValue();
output.add(outputRow);
}
return output;
}
/**
* Read all values from topic to List.
*
* @return List of output values.
*/
public List readValuesToList() {
final List output = new LinkedList<>();
V outputValue;
while (!isEmpty()) {
outputValue = readValue();
output.add(outputValue);
}
return output;
}
/**
* Get size of unread record in the topic queue.
*
* @return size of topic queue.
*/
public final long getQueueSize() {
return driver.getQueueSize(topic);
}
/**
* Verify if the topic queue is empty.
*
* @return {@code true} if no more record in the topic queue.
*/
public final boolean isEmpty() {
return driver.isEmpty(topic);
}
@Override
public String toString() {
return new StringJoiner(", ", TestOutputTopic.class.getSimpleName() + "[", "]")
.add("topic='" + topic + "'")
.add("keyDeserializer=" + keyDeserializer.getClass().getSimpleName())
.add("valueDeserializer=" + valueDeserializer.getClass().getSimpleName())
.add("size=" + getQueueSize())
.toString();
}
}