0

For testing the following IntegrationFlow:

IntegrationFlows.from("channel.input")
            .enrich(m -> m.header(MessageHeaders.ERROR_CHANNEL, "channel.input.error"))
            .handle("handler", "handle")
            .channel("channel.output")
            .get();

I wrote a configuration class:

@Configuration
@ComponentScan
@EnableIntegration
public class ServiceFlowContext {

    @Bean(name = "handler")
    public Handler handler() {
        return Mockito.mock(Handler.class);
    }

    @Bean("channel.output")
    public QueueChannel outputChannel() {
        return new QueueChannel();
    }
}

and a test class:

@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
@ContextConfiguration(classes = ServiceFlowContext.class)
public class ServiceFlowTest {
    @Autowired
    private Handler handler;

    @Autowired
    @Qualifier("channel.input")
    private MessageChannel inputChannel;

    @Autowired
    @Qualifier("channel.output")
    private QueueChannel outputChannel;

    @Test
    public void shouldGetMessageInErrorChannelIfHandlerFailed() {
        Message<String> message = MessageBuilder.withPayload("empty").build();
        when(handler.handle(message)).thenReturn(message);
        inputChannel.send(message);

        Message result = outputChannel.receive(5000);
        assertThat(result).isNotNull();
    }
}

The test will wait at the receive method for 5 seconds and I will get a null object which causes the test failed. However, if I define a real object instead of the mock object, just as:

public static class Handler1 {
    public Message<String> handle(Message<String> message) {
        return message;
    }
}

@Bean(name = "handler")
public Handler1 handler() {
    return new Handler1();
}

then, I can receive the message from channel.output channel (outputChannel) just as the same as what is sent. Are there any solutions to use mock handler in the test?

Tonny Tc
  • 852
  • 1
  • 12
  • 37

1 Answers1

1

You need to stub the handle() method.

Something like:

Handler handler = Mockito.mock(Handler.class);
BDDMockito.willAnswer(invocation -> invocation.getArgument(0))
    .given(handler).handle(any());
return handler;

That will do the same as your Handler1.handle().

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Thanks a lot, it works. But may I know further info. about the case please? I found [another question](https://stackoverflow.com/questions/36615330/mockito-doanswer-vs-thenreturn) said `doAnswer` and `thenReturn` should be the same in my case. – Tonny Tc Aug 09 '19 at 02:06
  • It's the same - just syntactic sugar - read [the javadocs for BDDMockito](https://static.javadoc.io/org.mockito/mockito-core/2.2.7/org/mockito/BDDMockito.html). Some people prefer the Behavior Driven Development style and prefer `given(...).willReturn(...)` over `when(...).thenReturn(...)` and `willAnswer(...).given(...)` over `doAnswer(...).when(...)`. – Gary Russell Aug 09 '19 at 12:58
  • That's strange. I've got `when(handler.handle(message)).thenReturn(message);` in the test method, but it doesn't work at all. – Tonny Tc Aug 09 '19 at 13:05
  • I suggest you edit your question to show the complete test case; even better, ask a new question, but you must show everything. – Gary Russell Aug 09 '19 at 13:19
  • I'm afraid I've put all things related to the test in my question. You can see in the second line of the test method called `shouldGetMessageInErrorChannelIfHandlerFailed`, there is code of `when(handler.handle(message)).thenReturn(message)`. And if it is as the same as `willAnswer(...).given(...)`, why I cannot get message from `outputChannel`? – Tonny Tc Aug 09 '19 at 13:30
  • Oh, sorry; I didn't see that; I prefer to do all the mocking in one place. It's because this `when(handler.handle(message))` will never pass because the message has been mutated (by the enricher) before it reaches the handler - hence you need the `doAnswer(...).when(handler).handle(any())` style. – Gary Russell Aug 09 '19 at 13:37
  • Thanks a lot for your advice, and I defined the behavior of the mock object in test is just because I want different behaviors for different test purposes... For the question itself, `when(...).then(...)` doesn't work is just because the `message` object will be changed in the IntegrationFlows which makes the mocked behavior not match, does it? – Tonny Tc Aug 09 '19 at 13:51
  • That's correct `handle(message)` gets a different message than the one specified in the mocking. And since you wanted the same behavior as the `Handler1` example, you have to capture the argument. – Gary Russell Aug 09 '19 at 14:05