5.8 Testing with `MockAdminClient`
① "MockAdminClient is instantiated with a list of brokers (here just one), and one broker that will be our controller.
"Apache Kafka provides a test class,
MockAdminClient, which you can initialize with any number of brokers and use to test that your applications behave correctly without having to run an actual Kafka cluster and really perform the admin operations on it."
⚠️ The stability caveat — stated honestly
"While
MockAdminClientis not part of the Kafka API and therefore subject to change without warning, it mocks methods that are public, and therefore the method signatures will remain compatible. There is a bit of a trade-off on whether the convenience of this class is worth the risk that it will change and break your tests, so keep this in mind."
What's good and what's missing
- ✓ “Some of the common methods have very comprehensive mocking: you can create topics with
MockAdminClient, and a subsequent call tolistTopics()will list the topics you ‘created’.” - ✗ “Not all methods are mocked. If you use AdminClient version 2.5 or earlier and call
incrementalAlterConfigs()of theMockAdminClient, you will get anUnsupportedOperationException” — “but you can handle this by injecting your own implementation.”
The class under test
public TopicCreator(AdminClient admin) {
this.admin = admin;
}
// Example of a method that will create a topic if its name starts with "test"
public void maybeCreateTopic(String topicName)
throws ExecutionException, InterruptedException {
Collection<NewTopic> topics = new ArrayList<>();
topics.add(new NewTopic(topicName, 1, (short) 1));
if (topicName.toLowerCase().startsWith("test")) {
admin.createTopics(topics);
// alter configs just to demonstrate a point
ConfigResource configResource =
new ConfigResource(ConfigResource.Type.TOPIC, topicName);
ConfigEntry compaction =
new ConfigEntry(TopicConfig.CLEANUP_POLICY_CONFIG,
TopicConfig.CLEANUP_POLICY_COMPACT);
Collection<AlterConfigOp> configOp = new ArrayList<AlterConfigOp>();
configOp.add(new AlterConfigOp(compaction, AlterConfigOp.OpType.SET));
Map<ConfigResource, Collection<AlterConfigOp>> alterConf = new HashMap<>();
alterConf.put(configResource, configOp);
admin.incrementalAlterConfigs(alterConf).all().get();
}
}Note the design that makes it testable: AdminClient is injected via the constructor. That's the only reason MockAdminClient can be substituted.
NOTE: "We are using the Mockito testing framework to verify that the
MockAdminClientmethods are called as expected and to fill in for the unimplemented methods."
Setup
@Before
public void setUp() {
Node broker = new Node(0,"localhost",9092);
this.admin = spy(new MockAdminClient(Collections.singletonList(broker),
broker)); // ①
// without this, the tests will throw
// `java.lang.UnsupportedOperationException: Not implemented yet`
AlterConfigsResult emptyResult = mock(AlterConfigsResult.class); // ②
doReturn(KafkaFuture.completedFuture(null)).when(emptyResult).all();
doReturn(emptyResult).when(admin).incrementalAlterConfigs(any());
}① "MockAdminClient is instantiated with a list of brokers (here just one), and one broker that will be our controller. The brokers are just the broker ID, hostname, and port — all fake, of course. No brokers will run while executing these tests. We'll use Mockito's spy injection, so we can later check that TopicCreator executed correctly."
② "we use Mockito's doReturn methods to make sure the mock admin client doesn't throw exceptions. The method we are testing expects the AlterConfigsResult object with an all() method that returns a KafkaFuture. We made sure that the fake incrementalAlterConfigs returns exactly that."
The tests
@Test
public void testCreateTestTopic()
throws ExecutionException, InterruptedException {
TopicCreator tc = new TopicCreator(admin);
tc.maybeCreateTopic("test.is.a.test.topic");
verify(admin, times(1)).createTopics(any()); // name starts with "test"
}
@Test
public void testNotTopic() throws ExecutionException, InterruptedException {
TopicCreator tc = new TopicCreator(admin);
tc.maybeCreateTopic("not.a.test");
verify(admin, never()).createTopics(any()); // must NOT be called
}The dependency you will forget
"Apache Kafka published
MockAdminClientin a test jar, so make sure yourpom.xmlincludes a test dependency:"
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>2.5.0</version>
<classifier>test</classifier> <!-- ← the easy-to-miss part -->
<scope>test</scope>
</dependency>