简体   繁体   English

Spring Boot Apache Camel Routes 测试

[英]Spring Boot Apache Camel Routes testing

I have a Springboot application, where I have some Camel routes configured.我有一个 Springboot 应用程序,其中配置了一些 Camel 路由。

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 .我想测试从activemq:testQueuequeueEventHandler::handleQueueEvent这条路线。 I tried different things mentioned here http://camel.apache.org/camel-test.html , but doesn't seem to get it working.我尝试了这里提到的不同内容http://camel.apache.org/camel-test.html ,但似乎没有让它工作。

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 .但我的 ProducerTemplate 始终为null I tried auto-wiring CamelContext , for which I get an exception saying it cannot resolve camelContext .我尝试自动连接CamelContext ,为此我收到一个异常,说它无法解析 camelContext But that can be resolved by adding SpringCamelContext.class to @SpringBootTest classes.但这可以通过将SpringCamelContext.class添加到@SpringBootTest类来解决。 But my ProducerTemplate is still null .但是我的ProducerTemplate仍然是null

Please suggest.请建议。 I am using Camel 2.18 and Spring Boot 1.4.我正在使用 Camel 2.18 和 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:在支持 Spring Boot 2 的 Camel 2.22.0 及后续版本中,您可以使用以下模板来测试支持 Spring Boot 2 的路由:

@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 . Spring Boot 区分@Configuration@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.如果在顶级类上注释,则初级将替换任何现有配置,而@TestConfiguration将在其他配置之外运行。

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.此外,在较大的项目中,您可能会遇到自动配置问题,因为您不能依赖 Spring Boot 2 来配置自定义数据库池或不正确的配置,或者在您具有特定目录结构且配置不在其中的情况下直接祖先目录。 In that case it is proabably preferable to omit the @EnableAutoConfiguration annotation.在这种情况下,省略@EnableAutoConfiguration注释可能更可取。 In order to tell Spring to still auto-configure Camel you can simply pass CamelAutoConfiguration.class to the classes mentioned in @SpringBootTest为了告诉Spring还是自动配置骆驼,你可以简单地传递CamelAutoConfiguration.class中提到的类@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.由于没有执行自动配置,Spring 不会在您的测试类中加载测试配置,也不会初始化 Camel。 By adding those configs to the boot classes manually Spring will do it for you.通过手动将这些配置添加到引导类,Spring 将为您完成。

For one route with MQ and Spring Boot like this:对于一个带有 MQ 和 Spring Boot 的路由,如下所示:

    @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:我使用adviceWith来替换端点并仅使用模拟

    @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.我使用以下依赖项:Spring Boot 2.1.4-RELEASE 和 Camel 2.23.2。 The complete source code is available on Github .完整的源代码可在Github 上获得

This is how I did this finally:这就是我最终这样做的方式:

    @RunWith(SpringRunner.class)
    public class CamelRouteConfigTest extends CamelTestSupport {
    
        private static final Logger LOG = LoggerFactory.getLogger(CamelRouteConfigTest.class);
        private static BrokerService brokerSvc = new BrokerService();
    
        @Mock
        private QueueEventHandler queueEventHandler;
    
        @BeforeClass
        // Sets up an embedded broker
        public static void setUpBroker() throws Exception {
            brokerSvc.setBrokerName("TestBroker");
            brokerSvc.addConnector("tcp://localhost:61616");
            brokerSvc.setPersistent(false);
            brokerSvc.setUseJmx(false);
            brokerSvc.start();
        }
    
        @Override
        protected RoutesBuilder createRouteBuilder() throws Exception {
            return new CamelConfig().route();
        }
    
        // properties in .yml has to be loaded manually. Not sure of .properties file
        @Override
        protected Properties useOverridePropertiesWithPropertiesComponent() {
            YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
            try {
                PropertySource<?> applicationYamlPropertySource = loader.load(
                    "properties", new ClassPathResource("application.yml"),null);// null indicated common properties for all profiles.
                Map source = ((MapPropertySource) applicationYamlPropertySource).getSource();
                Properties properties = new Properties();
                properties.putAll(source);
                return properties;
            } catch (IOException e) {
                LOG.error("application.yml file cannot be found.");
            }
    
            return null;
        }
    
        @Override
        protected JndiRegistry createRegistry() throws Exception {
            JndiRegistry jndi = super.createRegistry();
            MockitoAnnotations.initMocks(this);
            jndi.bind("queueEventHandler", queueEventHandler);
    
            return jndi;
        }
    
        @Test
        // Sleeping for a few seconds is necessary, because this line template.sendBody runs in a different thread and
        // CamelTest takes a few seconds to do the routing.
        public void testRoute() throws InterruptedException {
            template.sendBody("activemq:productpushevent", "HelloWorld!");
            Thread.sleep(2000);
            verify(queueEventHandler, times(1)).handleQueueEvent(any());
        }
    
        @AfterClass
        public static void shutDownBroker() throws Exception {
            brokerSvc.stop();
        }
    }

Did you try using Camel test runner?您是否尝试过使用 Camel 测试运行器?

@RunWith(CamelSpringJUnit4ClassRunner.class)

If you are using camel-spring-boot dependency, you may know that it uses auto configuration to setup Camel:如果你使用camel-spring-boot依赖,你可能知道它使用自动配置来设置Camel:

CamelAutoConfiguration.java

It means that you may also need to add @EnableAutoConfiguration to your test.这意味着您可能还需要将@EnableAutoConfiguration添加到您的测试中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM