Search code examples
spring-integration

How to test message-driven-channel-adapter with MockIntegrationContext


I am trying to test a Spring Integration flow that starts off from a message-driven-channel-adapter configured as:

<int-jms:message-driven-channel-adapter id="myAdapter" ... />

My test goes like:

@SpringJUnitConfig(locations = {"my-app-context.xml"})
@SpringIntegrationTest(noAutoStartup = {"myAdapter"})
public class MyIntegrationFlowTest {

  @Autowired
  private MockIntegrationContext mockIntegrationContext;

  @Test
  public void myTest() {
    ...
    MessageSource<String> messageSource = () -> new GenericMessage<>("foo");
    mockIntegrationContext.substituteMessageSourceFor("myAdapter", messageSource);
    ...
  }
}

I am however getting the following error:

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'myAdapter' is expected to be of type 'org.springframework.integration.endpoint.SourcePollingChannelAdapter' but was actually of type 'org.springframework.integration.jms.JmsMessageDrivenEndpoint'

How should one specify an alternate source for the channel adapter for testing using the MockIntegrationContext, or by some other method?


Solution

  • The Message Driver Channel Adapter is really not a Source Polling Channel Adapter. So, the substituteMessageSourceFor() is indeed cannot be used for that type of components, which, essentially is a MessageProducerSupport implementation, not a SourcePollingChannelAdapter for a MessageSource.

    The difference exists because not all protocols provides a listener-like hooks to spawn some self-managed task to subscribe to. The good example is JDBC, which is only passive system expecting requests. Therefore a polling channel adapter with a JdbcPollingChannelAdapter (which is a MessageSource) implementation must be used to interact with DB in event-driven manner.

    Other systems (like JMS in your case) provides some listener (or consumer) API for what we can spawn a while task (see MessageListenerContainer in spring-jms) and let its MessageProducerSupport to emit messages to the channel.

    Therefore you need to distinguish for yourself with what type of component you interact before choosing a testing strategy.

    Since there is no extra layer in case of message-driver channel adapter, but rather some specific, self-managed MessageProducerSupport impl, we not only provide a particular mocking API, but even don't require to know anything else, but just standard unit testing feature and a message channel this endpoint is producing in the configuration.

    So, the solution for you is something like:

    1. @SpringIntegrationTest(noAutoStartup = {"myAdapter"}) - that's fully correct in your code: we really have to stop the real channel adapter to not pollute our testing environment.

    2. You just need to inject into your test class a MessageChannel that id="myAdapter" is producing to. In your test code you just build a Message and send it into this channel. No need to worry about a MockIntegrationContext at all.