I am trying to unit test a simple flow where it is checking for existence of a file and then do some additional task.
IntegrationFlow
@Bean
public IntegrationFlow initiateAlarmAck() {
return IntegrationFlows.from("processAckAlarmInputChannel")
.handle((payload, headers) -> {
LOG.info("Received initiate ack alarm request at " + payload);
File watermarkFile = getWatermarkFile();
if(watermarkFile.isFile()){
LOG.info("Watermark File exists");
return true;
}else{
LOG.info("File does not exists");
return false;
}
})
.<Boolean, String>route(p -> fileRouterFlow(p))
.get();
}
File getWatermarkFile(){
return new File(eventWatermarkFile);
}
@Router
public String fileRouterFlow(boolean fileExits){
if(fileExits)
return "fileFoundChannel";
else
return "fileNotFoundChannel";
}
There is another integration flow which pick a message from fileNotFoundChannel
and does additional processing. I do not want to unit test this portion. How do I stop my test to not do further and stop after putting a message on fileNotFoundChannel
?
@Bean
public IntegrationFlow fileNotFoundFlow() {
return IntegrationFlows.from("fileNotFoundChannel")
.handle((payload, headers) -> {
LOG.info("File Not Found");
return payload;
})
.handle(this::getLatestAlarmEvent)
.handle(this::setWaterMarkEventInFile)
.channel("fileFoundChannel")
.get();
}
Unit Test Class
@RunWith(SpringRunner.class)
@Import(AcknowledgeAlarmEventFlow.class)
@ContextConfiguration(classes = {AlarmAPIApplication.class})
@PropertySource("classpath:application.properties ")
public class AcknowledgeAlarmEventFlowTest {
@Autowired
ApplicationContext applicationContext;
@Autowired
RestTemplate restTemplate;
@Autowired
@Qualifier("processAckAlarmInputChannel")
DirectChannel processAckAlarmInputChannel;
@Autowired
@Qualifier("fileNotFoundChannel")
DirectChannel fileNotFoundChannel;
@Autowired
@Qualifier("fileFoundChannel")
DirectChannel fileFoundChannel;
@Mock
File mockFile;
@Test
public void initiateAlarmAck_noFileFound_verifyMessageOnfileNotFoundChannel(){
AcknowledgeAlarmEventFlow.ProcessAcknowledgeAlarmGateway gateway = applicationContext.getBean(AcknowledgeAlarmEventFlow.ProcessAcknowledgeAlarmGateway.class);
gateway.initiateAcknowledgeAlarm();
processAckAlarmInputChannel.send(MessageBuilder.withPayload(new Date()).build());
MessageHandler mockMessageHandler = mock(MessageHandler.class);
fileNotFoundChannel.subscribe(mockMessageHandler);
verify(mockMessageHandler).handleMessage(any());
}
}
Thanks in advance
That is exactly scenario what we are doing now with the MockMessageHandler
implementation.
Looks like you go right way with mocking to prevent further action in that fileNotFoundFlow
, but miss some simple tricks:
You have to stop()
the real .handle((payload, headers) )
endpoint on that fileNotFoundChannel
. That way it will unsubscribe from the channel and won't consume messages any more. For this purpose I suggest to do:
return IntegrationFlows.from("fileNotFoundChannel")
.handle((payload, headers) -> {
LOG.info("File Not Found");
return payload;
}, e -> e.id("fileNotFoundEndpoint"))
And in the test class
@Autowired
@Qualifier("fileNotFoundEndpoint")
AbstractEndpoint fileNotFoundEndpoint;
...
@Test
public void initiateAlarmAck_noFileFound_verifyMessageOnfileNotFoundChannel(){
this.fileNotFoundEndpoint.stop();
MessageHandler mockMessageHandler = mock(MessageHandler.class);
fileNotFoundChannel.subscribe(mockMessageHandler);
AcknowledgeAlarmEventFlow.ProcessAcknowledgeAlarmGateway gateway = applicationContext.getBean(AcknowledgeAlarmEventFlow.ProcessAcknowledgeAlarmGateway.class);
gateway.initiateAcknowledgeAlarm();
processAckAlarmInputChannel.send(MessageBuilder.withPayload(new Date()).build());
verify(mockMessageHandler).handleMessage(any());
}
Pay, please, attention, how I have moved mocking and subscribing before sending message to the channel.
With the new MockIntegrationContext
features the Framework will take care about that stuff for you. But yeah... As with any unit test, the mocks must be prepared before interaction.
UPDATE
Working sample:
@RunWith(SpringRunner.class)
@ContextConfiguration
public class MockMessageHandlerTests {
@Autowired
private SubscribableChannel fileNotFoundChannel;
@Autowired
private AbstractEndpoint fileNotFoundEndpoint;
@Test
@SuppressWarnings("unchecked")
public void testMockMessageHandler() {
this.fileNotFoundEndpoint.stop();
MessageHandler mockMessageHandler = mock(MessageHandler.class);
this.fileNotFoundChannel.subscribe(mockMessageHandler);
GenericMessage<String> message = new GenericMessage<>("test");
this.fileNotFoundChannel.send(message);
ArgumentCaptor<Message<?>> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
verify(mockMessageHandler).handleMessage(messageArgumentCaptor.capture());
assertSame(message, messageArgumentCaptor.getValue());
}
@Configuration
@EnableIntegration
public static class Config {
@Bean
public IntegrationFlow fileNotFoundFlow() {
return IntegrationFlows.from("fileNotFoundChannel")
.<Object>handle((payload, headers) -> {
System.out.println(payload);
return payload;
}, e -> e.id("fileNotFoundEndpoint"))
.channel("fileFoundChannel")
.get();
}
}
}