1

I'm trying to unit test the callbacks from the kafkaTemplate.send() but its not working as expected. here's the code snippet of the code im trying to test.

    @Override
    public void sendMessage(String topicName, String message) {

        ListenableFuture<SendResult<String, String>> future = kafkaTemplate.send(topicName, message);
        future.addCallback(new ListenableFutureCallback<SendResult<String, String>>() {

            @Override
            public void onSuccess(SendResult<String, String> result) {
                System.out.print("Success")
            }
            @Override
            public void onFailure(Throwable ex) {
                System.out.print("Failed")
            }
        });
    }

and this is the unit test code


    private KafkaTemplate<String, String> kafkaTemplate;
    private KafkaService kafkaService;
    private SendResult<String, String> sendResult;
    private ListenableFuture<SendResult<String, String>> future;
    private RecordMetadata recordMetadata
    private String topicName
    private String message



    def setup() {
        kafkaTemplate = Mock(KafkaTemplate.class)
        kafkaService = new KafkaService(kafkaTemplate);
        topicName = "test.topic"
        message = "test message"
        sendResult = Mock(SendResult.class);
        future = Mock(ListenableFuture.class);
        recordMetadata = new RecordMetadata(new TopicPartition(topicName, 1), 1L, 0L, 0L, 0L, 0, 0);
    }

    def "Test success send message method"() {
        given:
        sendResult.getRecordMetadata() >> recordMetadata
        kafkaTemplate.send(_ as String, _ as String) >> future

        when:
        kafkaService.sendMessage(topicName, message)

        then:
        // catch success or failed here.

        1 * kafkaTemplate.send(_,_) >> {arguments ->
            final String topicNameParam = arguments.get(0)
            final String messageParam = arguments.get(1)

            assert topicNameParam == topicName
            assert messageParam == message
        }
    }

based on the debugger future is null on this scenario

ListenableFuture<SendResult<String, String>> future = kafkaTemplate.send(topicName, message); // future null
 future.addCallback(new ListenableFutureCallback<SendResult<String, String>>() { // future null

i already read a lot of answers here but it does not solved the issue or havent they explain it well that i would understand where the problem is. like this one https://stackoverflow.com/a/56677098

thanks for the help in advance!

ryan
  • 35
  • 2
  • 5
  • Welcome to SO. Please note that the preferred way to ask questions here is by including an [MCVE](https://stackoverflow.com/help/mcve) (please read!), i.e. a full set of application and test code, condensed down to be minimal, but reproducing your problem. As given by you, there is only an incoherent set of code snippets, no full classes with package names and imports, many unknown dependency classes etc. So nobody can reproduce your problem and I had to your job and work for 30 min in order to just create lots of dummy classes for making your code compile before writing my answer. – kriegaex Apr 09 '20 at 03:46
  • Thank you @kriegaex , i'll keep that in mind next time I ask a question here. – ryan Apr 10 '20 at 01:34

2 Answers2

1

You are making a typical Spock beginner's mistake when trying to combine mocking and stubbing: First declare a stub result in the given: block and later a checked mock interaction (without stub result) in the then: block. But mocking and stubbing always have to happen in the same interaction as described in the manual chapter I linked to. The manual also explains that the interaction in the then: block wins in your case, i.e. because you do not specify a stub result there, the result will default to null.

Furthermore, you make the argument verification much more difficult than necessary. Just use simple argument constraints instead. Your test will pass if you change it like this:

  def "Test success send message method"() {
    given:
    sendResult.getRecordMetadata() >> recordMetadata
//    kafkaTemplate.send(_ as String, _ as String) >> future

    when:
    kafkaService.sendMessage(topicName, message)

    then:
    // catch success or failed here.
    1 * kafkaTemplate.send(topicName, message) >> future
  }
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • 1
    Hi @kriegaex I got it working with your help, I also have a better understand with Stub and Mock based on your explanation and the link that you provide. this kind of answer is what im looking for, most of the answers in SO is giving the code but not explaining it clearly. – ryan Apr 10 '20 at 01:21
  • Though I face another issue, I cant trigger the onSuccess and onFailure callbacks. im trying a lot of answers here in also like these one https://stackoverflow.com/questions/56248612/how-to-write-unit-test-case-for-adding-callback-for-listenablefuture http://spockframework.org/spock/docs/1.2/interaction_based_testing.html#_returning_sequences_of_values should I create another SO question for this? – ryan Apr 10 '20 at 01:21
  • hi @kriegaex I move this to separate SO question. i tried to follow the MVCE https://stackoverflow.com/help/minimal-reproducible-example I hope its enough. and heres the link of my new question https://stackoverflow.com/q/61132766/13258992 thank you! – ryan Apr 10 '20 at 02:01
0

Since you are mocking the KafkaTemplate; you need to mock the send method to return a future.

I am not familiar with Spock.

With Mockito, this would be

SettableListenerFuture<SendResult<?, ?>> future = new SettableListenerFuture<>();
given(template.send(anyString(), anyString())).willReturn(future);
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Hi @Garry I did tried your suggestion. but it has the same result `ListenableFuture> future = kafkaTemplate.send(topicName, message);` is still being null based on the debugger. this the changes I did. ``` private SettableListenableFuture> future; def setup(){ ... future = new SettableListenableFuture<>(); } def "..."(){ given: ... kafkaTemplate.send(_ as String, _ as String) >> future } when: kafkaService.sendMessage(topicName, message) then: .... } ``` – ryan Apr 09 '20 at 00:34
  • I see you are new here; welcome! Don't put code in comments; it's unreadable - edit the question instead. Sorry, as I said, I don't know Spock so I can't help with that. – Gary Russell Apr 09 '20 at 01:15
  • Its okay @garry! Im still grateful that you look into this. i'll edit the question with your suggestion. again, thank you! – ryan Apr 09 '20 at 10:11