Search code examples
javaspringspring-integrationspring-integration-dslspring-integration-http

Spring Integration Testing of Outbound Http Enpoint


I am new to the Spring Integration project, now I need to create a flow with Java DSL and test it. I came up with these flows. First one should run by cron and invoke second one, which invokes HTTP endpoint and translates XML response to POJO:

  @Bean
  IntegrationFlow pollerFlow() {
    return IntegrationFlows
        .from(() -> new GenericMessage<>(""),
            e -> e.poller(p -> p.cron(this.cron)))
        .channel("pollingChannel")
        .get();
  }

  @Bean
  IntegrationFlow flow(HttpMessageHandlerSpec bulkEndpoint) {
    return IntegrationFlows
        .from("pollingChannel")
        .enrichHeaders(authorizationHeaderEnricher(user, password))
        .handle(bulkEndpoint)
        .transform(xmlTransformer())
        .channel("httpResponseChannel")
        .get();
  }

  @Bean
  HttpMessageHandlerSpec bulkEndpoint() {
    return Http
        .outboundGateway(uri)
        .httpMethod(HttpMethod.POST)
        .expectedResponseType(String.class)
        .errorHandler(new DefaultResponseErrorHandler());
  }

Now I want to test flow and mock HTTP call, but struggling to mock HTTP handler, I tried to do it like that:

@ExtendWith(SpringExtension.class)
@SpringIntegrationTest(noAutoStartup = {"pollerFlow"})
@ContextConfiguration(classes = FlowConfiguration.class)
public class FlowTests {

  @Autowired
  private MockIntegrationContext mockIntegrationContext;
  @Autowired
  public DirectChannel httpResponseChannel;
  @Autowired
  public DirectChannel pollingChannel;

  @Test
  void test() {
    final MockMessageHandler mockHandler = MockIntegration.mockMessageHandler()
        .handleNextAndReply(message -> new GenericMessage<>(xml, message.getHeaders()));
    mockIntegrationContext.substituteMessageHandlerFor("bulkEndpoint", mockHandler);
    httpResponseChannel.subscribe(message -> {
      assertThat(message.getPayload(), is(notNullValue()));
      assertThat(message.getPayload(), instanceOf(PartsSalesOpenRootElement.class));
    });

    pollingChannel.send(new GenericMessage<>(""));
  }
}

But I am always getting an error, that on line:

mockIntegrationContext.substituteMessageHandlerFor("bulkEndpoint", mockHandler);

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'bulkEndpoint' is expected to be of type 'org.springframework.integration.endpoint.IntegrationConsumer' but was actually of type 'org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler'

Am I doing something wrong here? I am assuming I have a problem with IntegrationFlow itself, or maybe my testing approach is a problem.


Solution

  • The error is correct. The bulkEndpoint is not an endpoint by itself. It is really a MessageHandler. The endpoint is created from the .handle(bulkEndpoint). See docs: https://docs.spring.io/spring-integration/docs/current/reference/html/overview.html#finding-class-names-for-java-and-dsl-configuration and https://docs.spring.io/spring-integration/docs/current/reference/html/testing.html#testing-mocks.

    So, to make it working you need to do something like this:

    .handle(bulkEndpoint, e -> e.id("actualEndpoint"))
    

    And then in the test:

    mockIntegrationContext.substituteMessageHandlerFor("actualEndpoint", mockHandler);
    

    You also probably need to think to not have that pollerFlow to be started when you test it sine you send the message into pollingChannel manually. So, there is no conflicts with what you'd like to test. For this reason you also add a id() into your e.poller(p -> p.cron(this.cron)) and use @SpringIntegrationTest(noAutoStartup) to have it stopped before your test. I see you try noAutoStartup = {"pollerFlow"}, but this is not going to help for static flows. You indeed need to have stopped an actual endpoint in this case.