Spring Boot Apache Camel Routes testing

I have a Springboot application, where I have some Camel routes configured.

public class CamelConfig {
    private static final Logger LOG = LoggerFactory.getLogger(CamelConfig.class);

    @Value("${activemq.broker.url:tcp://localhost:61616}")
    String brokerUrl;

    @Value("${activemq.broker.maxconnections:1}")
    int maxConnections;

    @Bean
    ConnectionFactory jmsConnectionFactory() {
        PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(new ActiveMQConnectionFactory(brokerUrl));
        pooledConnectionFactory.setMaxConnections(maxConnections);
        return pooledConnectionFactory;
    }

    @Bean
    public RoutesBuilder route() {
        LOG.info("Initializing camel routes......................");
        return new SpringRouteBuilder() {
            @Override
            public void configure() throws Exception {
                from("activemq:testQueue")
                  .to("bean:queueEventHandler?method=handleQueueEvent");
            }
        };
    }
}

I want to test this route from activemq:testQueue to queueEventHandler::handleQueueEvent. I tried different things mentioned here http://camel.apache.org/camel-test.html, but doesn't seem to get it working.

I am trying to do something like this:

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = {CamelConfig.class,   CamelTestContextBootstrapper.class})
    public class CamelRouteConfigTest {

    @Produce(uri = "activemq:testQueue")
    protected ProducerTemplate template;

    @Test
    public void testSendMatchingMessage() throws Exception {
        template.sendBodyAndHeader("testJson", "foo", "bar");
        // Verify handleQueueEvent(...) method is called on bean queueEventHandler by mocking
    }

But my ProducerTemplate is always null. I tried auto-wiring CamelContext, for which I get an exception saying it cannot resolve camelContext. But that can be resolved by adding SpringCamelContext.class to @SpringBootTest classes. But my ProducerTemplate is still null.

Please suggest. I am using Camel 2.18 and Spring Boot 1.4.


In Camel 2.22.0 and ongoing, which supports Spring Boot 2 you can use the following template to test your routes with Spring Boot 2 support:

@RunWith(CamelSpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.NONE, classes = {
    Route1.class,
    Route2.class,
    ...
})
@EnableAutoConfiguration
@DisableJmx
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class RouteTest {

  @TestConfiguration
  static class Config {
    @Bean
    CamelContextConfiguration contextConfiguration() {
      return new CamelContextConfiguration() {
        @Override
        public void beforeApplicationStart(CamelContext camelContext) {
          // configure Camel here
        }

        @Override
        public void afterApplicationStart(CamelContext camelContext) {
          // Start your manual routes here
        }
      };
    }

    @Bean
    RouteBuilder routeBuilder() {
      return new RouteBuilder() {
        @Override
        public void configure() {
          from("direct:someEndpoint").to("mock:done");
        }
      };
    }

    // further beans ...
  }

  @Produce(uri = "direct:start")
  private ProducerTemplate template;
  @EndpointInject(uri = "mock:done")
  private MockEndpoint mockDone;

  @Test
  public void testCamelRoute() throws Exception {
    mockDone.expectedMessageCount(1);

    Map<String, Object> headers = new HashMap<>();
    ...
    template.sendBodyAndHeaders("test", headers);

    mockDone.assertIsSatisfied();
  }
}

Spring Boot distinguishes between @Configuration and @TestConfiguration. The primer one will replace any existing configuration, if annotated on a top-level class, while @TestConfiguration will be run in addition to the other configurations.

Further, in larger projects you might run into auto-configuration issues as you can't rely on Spring Boot 2 to configure your custom database pooling or what not correctly or in cases where you have a specific directory structure and the configurations are not located within a direct ancestor directory. In that case it is proabably preferable to omit the @EnableAutoConfiguration annotation. In order to tell Spring to still auto-configure Camel you can simply pass CamelAutoConfiguration.class to the classes mentioned in @SpringBootTest

@SpringBootTest(webEnvironment = WebEnvironment.NONE, classes = {
    Route1.class,
    Route2.class,
    RouteTest.Config.class,
    CamelAutoConfiguration.class
}

As no automatic configuration is performed, Spring won't load the test configuration inside your test class nor initialize Camel as well. By adding those configs to the boot classes manually Spring will do it for you.


For one route with MQ and Spring Boot like this:

    @Component
    public class InboundRoute extends RouteBuilder {
    
      @Override
      public void configure() {
        JaxbDataFormat personDataFormat = new JaxbDataFormat();
        personDataFormat.setContextPath(Person.class.getPackage().getName());
        personDataFormat.setPrettyPrint(true);
        from("direct:start").id("InboundRoute")
            .log("inbound route")
            .marshal(personDataFormat)
            .to("log:com.company.app?showAll=true&multiline=true")
            .convertBodyTo(String.class)
            .inOnly("mq:q.empi.deim.in")
            .transform(constant("DONE"));
      }
    }

I use adviceWith in order to replace the endpoint and use only mocks:

    @RunWith(CamelSpringBootRunner.class)
    @UseAdviceWith
    @SpringBootTest(classes = InboundApp.class)
    @MockEndpoints("mock:a")
    public class InboundRouteCamelTest {
    
      @EndpointInject(uri = "mock:a")
      private MockEndpoint mock;
    
      @Produce(uri = "direct:start")
      private ProducerTemplate template;
    
      @Autowired
      private CamelContext context;
    
      @Test
      public void whenInboundRouteIsCalled_thenSuccess() throws Exception {
        mock.expectedMinimumMessageCount(1);
        RouteDefinition route = context.getRouteDefinition("InboundRoute");
        route.adviceWith(context, new AdviceWithRouteBuilder() {
          @Override
          public void configure() {
            weaveByToUri("mq:q.empi.deim.in").replace().to("mock:a");
          }
        });
        context.start();
    
        String response = (String) template.requestBodyAndHeader("direct:start",
            getSampleMessage("/SimplePatient.xml"), Exchange.CONTENT_TYPE, MediaType.APPLICATION_XML);
    
        assertThat(response).isEqualTo("DONE");
        mock.assertIsSatisfied();
      }
    
      private String getSampleMessage(String filename) throws Exception {
        return IOUtils
            .toString(this.getClass().getResourceAsStream(filename), StandardCharsets.UTF_8.name());
      }
    }

I use the following dependencies: Spring Boot 2.1.4-RELEASE and Camel 2.23.2. The complete source code is available on Github.