0

Here is my producer send method that I'm writing a unit test for:

public void send(final ReadingForecastRequest requestStub) {
        logger.info("{}::send()", getClass().getCanonicalName());
        final ProducerRecord<String, ReadingForecastRequest> record = new ProducerRecord<>(config.topicName, requestStub);
        String context = requestStub.getHeader().getContext();
        Long trackingId = requestStub.getHeader().getTrackingId();
        record.headers().add("context", context.getBytes());
        record.headers().add("trackingId", Longs.toByteArray(trackingId));
        ListenableFuture future = template.send((ProducerRecord<String, Req>) record);

        future.addCallback(new ListenableFutureCallback<SendResult<String, Req>>() {

            @Override
            public void onSuccess(SendResult<String, Req> result) {
                logger.info("Sent message=[" + requestStub +
                    "] with offset=[" + result.getRecordMetadata().offset() + "]");
            }

            @Override
            public void onFailure(Throwable ex) {
                logger.error("Unable to send message=["
                    + requestStub + "] due to : " + ex.getMessage());
            }
        });
    }

Here is my test

public class ProducerTest {

    final KafkaTemplate<String, Integer> template = createKafkaTemplateMock();
    final String topicName = UUID.randomUUID().toString();
    final int sendTimeout = 1;
    final Producer producer = createProducer(topicName, sendTimeout);

    @Test
    @SuppressWarnings("unchecked")
    public void testSuccessfulSend() {
        ProducerRecord<String, String> record = mock(ProducerRecord.class);
        RecordMetadata recordMetadata = new RecordMetadata(new TopicPartition(topicName, 0), 0, 0L, 0L, 0L, 0, 0);
        SettableListenableFuture<SendResult<String, String>> future = new SettableListenableFuture<>();
        final ReadingForecastRequest request = new ReadingForecastRequest(0, 0, 1.0, 20, new Double[] { 567.8, 976.5 }, header);
        when(template.send((ProducerRecord) any())).thenReturn(future);
        producer.send(request);
        future.set(new SendResult<String, String>(record, recordMetadata));
        verify(future, times(1)).onSuccess(SendResult.class);
    }
}

My question is on the last line of my test (which doesn't work). I'm not sure what to assert. I want to just check that the onSuccess method within the callback ran to see that the send was successful, but it's not recognizing "onSuccess". Is there something else I should be asserting?

I based my test on this stackoverflow question, which in the edited answer, captures future output and tests that. I'm having trouble understanding how that really tests the method and how to see what that output would be (e.g. for a successful run) if I were to test that.

sFishman
  • 137
  • 9
  • The code under test does not call `onSuccess()` on the `Future`, so why should we verify that it is called? It is not the responsibility of the code under test. – Turing85 Mar 05 '23 at 10:35
  • Good point. You're saying my future variable in my test is not the same as the future that's returned from template.send inside of my method under test. Correct? Is there any way to capture and test the future that gets returned inside of the my producer.send() method? – sFishman Mar 05 '23 at 10:42
  • And if not, how is making a fake "future" and just manually setting it's value a good test? I'm setting the future's value and then I'll test that it's value is what I just set it to be? That doesn't seem to test my method. – sFishman Mar 05 '23 at 10:42
  • "*you're saying my future variable in my test is not the same as the future that's returned from template.send*" - No. I said that the code under test does not call `.onSuccess(...)` on the `Future`. It is not the reponsibility of the code under test to call `onSuccess(...)` and should thus not be tested. – Turing85 Mar 05 '23 at 10:45
  • Ok. I understand now. Good point. So what should be tested? – sFishman Mar 05 '23 at 11:15
  • 1
    This is borderline opinion-based. If I were the Product Owner, I would rather have tests on other, more meaningful, parts of the code base. --- Please read: [Can I ask only one question per post?](https://meta.stackexchange.com/questions/222735/can-i-ask-only-one-question-per-post) – Turing85 Mar 05 '23 at 11:19
  • Have you tried looking at the source code of Spring Kafka for their own tests? More specifically, what exactly are you testing here? send will be called and return a Future, there's no doubt about that, so what are you actually trying to assert specific to your own application? If you want to test async code, then first try `.get()` the future, but you've not injected any logger to capture/test what gets printed – OneCricketeer Mar 05 '23 at 15:37

0 Answers0