Модульный тест Spring Integration flow DSL

Я пытаюсь выполнить модульное тестирование простого потока, в котором он проверяет наличие файла, а затем выполняет некоторые дополнительные задачи.

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";
}

Есть еще один поток интеграции, который выбирает сообщение из fileNotFoundChannel и выполняет дополнительную обработку. Я не хочу проводить модульное тестирование этой части. Как мне остановить мой тест, чтобы он не выполнялся дальше, и прекратить после отправки сообщения на 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();
}

Класс модульного теста

@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());
    }
}

заранее спасибо


person nkare    schedule 17.05.2017    source источник


Ответы (1)


Это именно тот сценарий, который мы делаем сейчас с реализацией MockMessageHandler.

Похоже, вы идете правильным путем с издевательством, чтобы предотвратить дальнейшие действия в этом fileNotFoundFlow, но упускаете несколько простых трюков:

Вы должны stop() реальную .handle((payload, headers) ) конечную точку на этом fileNotFoundChannel. Таким образом, он откажется от подписки на канал и больше не будет получать сообщения. Для этого предлагаю сделать:

return IntegrationFlows.from("fileNotFoundChannel")
.handle((payload, headers) ->  {
  LOG.info("File Not Found");
  return payload;
}, e -> e.id("fileNotFoundEndpoint"))

И в тестовом классе

@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());
}

Обратите внимание, как я переместил насмешку и подписку перед отправкой сообщения на канал.

Благодаря новым MockIntegrationContext функциям Framework обо всем позаботится за вас. Но да ... Как и в случае с любым модульным тестом, mocks нужно подготовить до взаимодействия.

ОБНОВЛЕНИЕ

Рабочий образец:

@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();
    }

}

}
person Artem Bilan    schedule 17.05.2017
comment
Исправил тест и понял, что fileNotFoundChannel - это прямой канал. Я изменил fileNotFoundChannel на SubscribableChannel, чтобы в тестовом примере можно было подписаться на канал mockMessageHandler. Однако элемент управления не приходит на проверку строки в тестовом примере. Я вижу, что preSend вызывается в 'fileNotFoundChannel' - person nkare; 18.05.2017
comment
Я добавил к своему ответу полностью рабочий образец. Перед издевательством убедитесь, что вы остановили конечную точку. В противном случае у вашего fileNotFoundChannel будет несколько подписчиков, и первый поймает первое сообщение. См. docs.spring. io / spring-integration / reference / html / о LoadBalancingStrategy - person Artem Bilan; 18.05.2017
comment
Спасибо за направление. У меня есть несколько тестов в тестовом классе, которые проверяют конечные точки индивидуально. При индивидуальном тестировании они работали хорошо, но при запуске в одном тестовом классе некоторые из них терпели неудачу. Для каждого теста, который останавливает конечную точку, мне приходилось начинать в конце теста. Кроме того, когда макет обработчика подписывается на канал, мне приходилось отписываться от него в конце теста. Это было необходимо для того, чтобы все тесты выполнялись в одном тестовом классе. - person nkare; 20.05.2017
comment
Это правда: если вы подписываетесь макетом, вам придется в конце концов отказаться от подписки. Поэтому в нашем тестовом фреймворке resetBeans() параметр - person Artem Bilan; 20.05.2017
comment
Можем ли мы использовать @SpringBootTest с @SpringIntegrationTest? Документы на удивление не упомянули об этом. - person Abhijit Sarkar; 05.12.2017