Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

CDI Testing

Testing is a crucial part of any development or integration work. In case you're using the Camel CDI integration for your applications, you have a number of options to ease testing.

...

Code Block
languagexml
<dependency>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-test-cdi</artifactId>
    <scope>test</test>scope>
    <version>x.x.x</version>
    <!-- use the same version as your Camel core version -->
</dependency>

...

Code Block
languagejava
@RunWith(CamelCdiRunner.class)
public class CamelCdiRunnerTestCamelCdiTest {

    @Inject
    CamelContext context;

    @Test
    public void test() {
        assertThat("Camel context status is incorrect!",
            context.getStatus(),
            is(equalTo(ServiceStatus.Started)));
    }
}

...

Code Block
languagejava
@RunWith(CamelCdiRunner.class)
public class CamelCdiRunnerTestCamelCdiTest {

    @Test
    public void test(@Uri("direct:foo") ProducerTemplate producer) {
        producer.sendBody("bar");
    }
}

Camel CDI test provides the @Order annotation that you can use to execute the test methods in a particular sequence, e.g.: 

Code Block
languagejava
@RunWith(CamelCdiRunner.class)
public class CamelCdiRunnerTestCamelCdiTest {
 
    @Test
    @Order(1)
    public void firstTestMethod() {
    }
 
    @Test
    @Order(2)
    public void secondTestMethod() {
    }
}

One CDI container is bootstrapped for the entire execution of the test class. 

Besides, the test class is deployed as a CDI bean, so that you can control how the runner instantiate the test class, either one test class instance for each test method (the default, depending on the built-in default @Dependent CDI scope), or one test class instance for the entire test class execution using the @ApplicationScoped scope, e.g.:

Code Block
languagejava
@ApplicationScoped
@RunWith(CamelCdiRunner.class)
public class CamelCdiRunnerTestCamelCdiTest {
 
    int counter;

    @Test
    @Order(1)
    public void firstTestMethod() {
        counter++;
    }
 
    @Test
    @Order(2)
    public void secondTestMethod() {
        assertEquals(counter, 1);
    }
}

Arquillian

With this approachIn case you need to add additional test beans, you can use the JUnit runner or TestNG support @Beans annotation provided by Arquillian to delegate the bootstrap of the CDI container. You need to declare a @Deployment method to create your application configuration to be deployed in the container using ShrinkWrap descriptors, e.Camel CDI test. For example, if you need to add a route to your Camel context, instead of declaring a RouteBuilder bean with a nested class, you can declare a managed bean, e.g.:

Code Block
languagejava
@RunWith(Arquillian.class)
public class CamelCdiJavaSeTestclass TestRoute extends RouteBuilder {

    @Deployment@Override
    public staticvoid Archive deploymentconfigure() {
        return ShrinkWrap.create(JavaArchive.class)from("direct:foo").to("mock:bar");
    }
}

And add it with the @Beans annotation, e.g.:

Code Block
languagejava
@RunWith(CamelCdiRunner.class)
@Beans(classes = TestRoute.class)
public class CamelCdiTest {

}

Arquillian

With this approach, you use the JUnit runner or TestNG support provided by Arquillian to delegate the bootstrap of the CDI container. You need to declare a @Deployment method to create your application configuration to be deployed in the container using ShrinkWrap descriptors, e.g.:

Code Block
languagejava
@RunWith(Arquillian.class)
public class CamelCdiJavaSeTest {

    @Deployment
    public static Archive deployment() {        // Camel CDI
            .addPackage(CdiCamelExtension.class.getPackage())
            // Test classes
            .addPackage(Application.class.getPackage())
            // Bean archive deployment descriptor
        return    ShrinkWrap.addAsManifestResourcecreate(EmptyAsset.INSTANCE, "beans.xml");JavaArchive.class)
    }
 
    @Inject
    CamelContext context;

// Camel CDI
      @Test
    public void test() {
 .addPackage(CdiCamelExtension.class.getPackage())
         assertThat("Camel context status is incorrect!",// Test classes
            context.getStatus(),.addPackage(Application.class.getPackage())
            // Bean archive deployment descriptor
            is(equalTo(ServiceStatus.Started)).addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
    }
}

In that example, you can use any Java SE Arquillian embedded container adapter, like the Weld embedded container adapter e.g. with Maven you need that complete set of dependencies:

Code Block
languagexml
<dependencies>
 
    @Inject
    CamelContext context;

    <dependency>@Test
    public void  <groupId>org.jboss.arquillian.junit</groupId>test() {
      <artifactId>arquillian-junit-container</artifactId>
  assertThat("Camel context status  <scope>test</scope>
    </dependency>

is incorrect!",
      <dependency>
      <groupId>org.jboss.shrinkwrap.descriptors</groupId>context.getStatus(),
      <artifactId>shrinkwrap-descriptors-depchain</artifactId>
      <type>pom</type>is(equalTo(ServiceStatus.Started)));
    }
}

In that example, you can use any Java SE Arquillian embedded container adapter, like the Weld embedded container adapter e.g. with Maven you need that complete set of dependencies:

Code Block
languagexml
<dependencies>

      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.jboss.arquillian.container<junit</groupId>
      <artifactId>arquillian-weld-se-embedded-1.1<junit-container</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.jboss.weld<shrinkwrap.descriptors</groupId>
      <artifactId>weld<artifactId>shrinkwrap-descriptors-core<depchain</artifactId>
      <type>pom</type>
      <scope>test</scope>
    </dependency>

</dependencies>

Using ShrinkWarp Descriptors, you have a complete control over the configuration and kind of Camel CDI applications you want to test. For example, to test a Camel CDI application that uses the Camel REST DSL configured with the Servlet component, you need to create a Web archive, e.g.:

Code Block
languagejava
@RunWith(Arquillian.class)
public class CamelCdiWebTest {

    @Deployment    <dependency>
      <groupId>org.jboss.arquillian.container</groupId>
    public static Archive<?> createTestArchive() {
 <artifactId>arquillian-weld-se-embedded-1.1</artifactId>
      <scope>test</scope>
    return ShrinkWrap.create(WebArchive.class)</dependency>

    <dependency>
        .addClass(Application.class)<groupId>org.jboss.weld</groupId>
      <artifactId>weld-core</artifactId>
      .addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"))<scope>test</scope>
    </dependency>

</dependencies>

Using ShrinkWarp Descriptors, you have a complete control over the configuration and kind of Camel CDI applications you want to test. For example, to test a Camel CDI application that uses the Camel REST DSL configured with the Servlet component, you need to create a Web archive, e.g.:

Code Block
languagejava
@RunWith(Arquillian.class)
public class CamelCdiWebTest {
        .setWebXML(Paths.get("src/main/webapp/WEB-INF/web.xml").toFile());
    }

    @Test
    @RunAsClient@Deployment
    public void test(@ArquillianResource URL url) throws Exceptionstatic Archive<?> createTestArchive() {
        return assertThat(IOHelperShrinkWrap.loadText(new URL(url, "camel/rest/hello").openStream()),create(WebArchive.class)
            is(equalTo("Hello World!\n")));
    }
}

In the example above, you can use any Arquillian Web container adapter, like the Jetty embedded container adapter e.g. with Maven you need the complete following set of dependencies:

.addClass(Application.class)
            .addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("beans.xml"))
            .setWebXML(Paths.get("src/main/webapp/WEB-INF/web.xml").toFile());
    }

    @Test
    @RunAsClient
    public void test(@ArquillianResource URL url) throws Exception {
        assertThat(IOHelper.loadText(new URL(url, "camel/rest/hello").openStream()),
            is(equalTo("Hello World!\n")));
    }
}

In the example above, you can use any Arquillian Web container adapter, like the Jetty embedded container adapter e.g. with Maven you need the complete following set of dependencies:

Code Block
languagexml
</dependencies>
Code Block
languagexml
</dependencies>
 
  <dependency>
    <groupId>org.jboss.arquillian.junit</groupId>
    <artifactId>arquillian-junit-container</artifactId>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.jboss.arquillian.testenricher</groupId>
    <artifactId>arquillian-testenricher-resource</artifactId>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.jboss.shrinkwrap.descriptors</groupId>
    <artifactId>shrinkwrap-descriptors-depchain</artifactId>
    <type>pom</type>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.jboss.weld.servlet</groupId>
    <artifactId>weld-servlet</artifactId>
    <scope>test</scope>
  </dependency>
 
  <dependency>
    <groupId>org.eclipsejboss.arquillian.jetty<junit</groupId>
    <artifactId>jetty<artifactId>arquillian-junit-webapp<container</artifactId>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.eclipsejboss.arquillian.jetty<testenricher</groupId>
    <artifactId>jetty<artifactId>arquillian-testenricher-annotations<resource</artifactId>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.jboss.arquillianshrinkwrap.container<descriptors</groupId>
    <artifactId>arquillian<artifactId>shrinkwrap-jettydescriptors-embedded-9<depchain</artifactId>
    <type>pom</type>
    <scope>test</scope>
  </dependency>

</dependencies>

You can see the tests in the camel-example-cdi-rest-servlet example for a complete working example of testing a Camel CDI application using the REST DSL and deployed as a WAR in Jetty.

PAX Exam

If you target OSGi as runtime environment for your Camel CDI applications, you can use PAX Exam to automate the deployment of your tests into an OSGi container, for example into Karaf, e.g.:

  <dependency>
    <groupId>org.jboss.weld.servlet</groupId>
    <artifactId>weld-servlet</artifactId>
    <scope>test</scope>
  </dependency>
 
  <dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-webapp</artifactId>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-annotations</artifactId>
    <scope>test</scope>
  </dependency>

  <dependency>
    <groupId>org.jboss.arquillian.container</groupId>
    <artifactId>arquillian-jetty-embedded-9</artifactId>
    <scope>test</scope>
  </dependency>

</dependencies>

You can see the tests in the camel-example-cdi-rest-servlet example for a complete working example of testing a Camel CDI application using the REST DSL and deployed as a WAR in Jetty.

PAX Exam

If you target OSGi as runtime environment for your Camel CDI applications, you can use PAX Exam to automate the deployment of your tests into an OSGi container, for example into Karaf, e.g.:

Code Block
languagejava
@RunWith(PaxExam.class)
@ExamReactorStrategy(PerClass.class)
public class PaxCdiOsgiTest {

    @Configuration
    public Option[] config() throws IOException {
Code Block
languagejava
@RunWith(PaxExam.class)
@ExamReactorStrategy(PerClass.class)
public class PaxCdiOsgiTest {

    @Configuration
    public Option[] config() throws IOException {
        return options(
            // Karaf configuration
            karafDistributionConfiguration()
                .frameworkUrl(
                    maven()
          return options(
            .groupId("org.apache.karaf"// Karaf configuration
            karafDistributionConfiguration()
                .frameworkUrl(
                    maven()
                       .artifactIdgroupId("org.apache-.karaf")
                       .versionAsInProject()
                       .type("zip"))
                .name("Apache Karaf")
                .unpackDirectory(new File("target/paxexam/unpack/")),
            // PAX CDI Weld
            features(
                maven().artifactId("apache-karaf")
                       .versionAsInProject()
                       .type("zip"))
                .name("Apache Karaf")
                .unpackDirectory(new File("target/paxexam/unpack/")),
            // PAX CDI Weld
            features(
                maven()
                    .groupId("org.ops4j.pax.cdi")
                    .artifactId("pax-cdi-features")
                    .type("xml")
                    .classifier("features")
                    .versionAsInProject(),
                "pax-cdi-weld"),
            // Karaf Camel commands
            mavenBundle()
                .groupId("your.application.groupId")
                .artifactId("your.application.artifactId")
                .versionAsInProject()
        );
    }
 
    @Inject
    private CamelContext context;

    @Test
    public void testContextStatus() {
        assertThat("Camel context status is incorrect!",
            context.getStatus(), equalTo(ServiceStatus.Started));
    }
}

You can see the tests in the camel-example-cdi-osgi example for a complete working example of testing a Camel CDI application deployed in an OSGi container using PAX Exam.

Testing Patterns

You can see the tests in the camel-example-cdi-test example for a thorough overview of the following testing patterns for Camel CDI applications.

Info

While the patterns above are illustrated using the Camel CDI test module, they should equally work with Arquillian and PAX Exam unless otherwise stated or illustrated with a specific example.

Test routes

You may want to add some Camel routes to your Camel CDI applications for testing purpose. For example to route some exchanges to a MockEndpoint instance. You can do that by declaring a RouteBuilder bean within the test class as you would normally do in your application code, e.g.:

Code Block
languagejava
@RunWith(CamelCdiRunner.class)
public class CamelCdiTest {
 
    // Declare a RouteBuilder bean for testing purpose
    // that is automatically added to the Camel context
    static class TestRoute extends RouteBuilder {

    @Override
    public void configure() {
        from("direct:out").routeId("test").to("mock:out");
    }
 
    // And retrieve the MockEndpoint  .groupId("org.ops4j.pax.cdi")for further assertions
    @Inject
    @Uri("mock:out")
    MockEndpoint mock;
}

You can find more information in auto-detecting Camel routes.

In case you prefer declaring the RouteBuilder bean in a separate class, for example to share it more easily across multiple test classes, you can use the @Beans annotation to instruct Camel CDI test to deploy that class as a CDI bean, e.g.:

Code Block
languagejava
@RunWith(CamelCdiRunner.class)
@Beans(classes = TestRoute.class)
public class CamelCdiTest {

    // ...
}

Bean alternatives

You may want to replace a bean that is used in your Camel routes by another bean for testing purpose, for example to mock it or change the behaviour of the application bean.

Imagine you have the following route in your application:

Code Block
languagejava
public class Application {

    @ContextName("camel-test-cdi")
    static class Hello extends RouteBuilder {
         .artifactId("pax-cdi-features")
                    .type("xml")
                    .classifier("features")
                    .versionAsInProject(),
                "pax-cdi-weld"),
            // Karaf Camel commands
            mavenBundle()
     @Override
        public void  .groupId("your.application.groupId")configure() {
                .artifactId("your.application.artifactId")from("direct:in").bean("bean").to("direct:out");
        }
    }
}

And the corresponding bean:

Code Block
languagejava
@Named("bean")
public class   .versionAsInProject()Bean {

    public String process(@Body String body);
    }
  {
    @Inject
    privatereturn CamelContext contextbody;

    }
}

Then you can replace the bean above in your tests by declaring an alternative bean, annotated with @Alternative, e.g.:

Code Block
languagejava
@Alternative
@Named("bean")
public class AlternativeBean {
@Test
    public void testContextStatus() {
    public String   assertThat("Camel context status is incorrect!",process(@Body String body) {
        return body +  context.getStatus(), equalTo(ServiceStatus.Started))" with alternative bean!";
    }
}

You can see the tests in the camel-example-cdi-osgi example for a complete working example of testing a Camel CDI application deployed in an OSGi container using PAX Exam.

Testing Patterns

You can see the tests in the camel-example-cdi-test example for a thorough overview of the following testing patterns for Camel CDI applications.

Test routes

And you need to activate (a.k.a. select in CDI terminology) this alternative bean in your tests. If your using the CamelCdiRunner JUnit runner, you can do that with the @Beans annotation provided by the Camel CDI test moduleYou may want to add some Camel routes to your Camel CDI applications for testing purpose. For example to route some exchanges to a MockEndpoint instance. You can do that by declaring a RouteBuilder bean within the test class as you would normally do in your application code, e.g.:

Code Block
languagejava
@RunWith(CamelCdiRunner.class)
@Beans(alternatives = AlternativeBean.class)
public class CamelCdiTest {
 
    // Declare a RouteBuilder bean for testing purpose
    // that is automatically added to the Camel context
@Test
    public void testAlternativeBean(@Uri("direct:in") ProducerTemplate producer
              static class TestRoute extends RouteBuilder {

    @Override
    public void configure() {
        from@Uri("direct:out").routeId("test").to("mock:out");
    }
 
    // And retrieve the MockEndpoint for further assertionsMockEndpoint mock) throws InterruptedException {
    @Inject
    @Uri("mock:out")mock.expectedMessageCount(1);
    MockEndpoint mock;
}

You can find more information in auto-detecting Camel routes.

Bean alternatives

You may want to replace a bean that is used in your Camel routes by another bean for testing purpose, for example to mock it or change the behaviour of the application bean.

Imagine you have the following route in your application:

Code Block
languagejava
public class Application {    mock.expectedBodiesReceived("test with alternative bean!");

    @ContextName    producer.sendBody("camel-test-cdi")test");

        MockEndpoint.assertIsSatisfied(1L, TimeUnit.SECONDS, mock);
    }

    static class HelloTestRoute extends RouteBuilder {

        @Override
        public void configure() {
            from("direct:inout").beanrouteId("beantest").to("direct:out");
        }
    }

And the corresponding bean:

Code Block
languagejava
@Named("bean")
public class Bean {

    public String process(@Body String body) {("mock:out");
        return body;}
    }
}

Then you can replace the bean above in your tests by declaring an alternative bean, annotated with @Alternative, If you're using Arquillian as testing framework, you need to activate the alternative in your deployment method, e.g.: 

Code Block
languagejava
@Alternative
@Named("bean"@RunWith(Arquillian.class)
public class AlternativeBeanCamelCdiTest {

    public String process(@Body String body) { @Deployment
    public static Archive deployment() return{
 body + " with alternative bean!";
    }
}

And you need to activate (a.k.a. select in CDI terminology) this alternative bean in your tests. If your using the CamelCdiRunner JUnit runner, you can do that with the @Beans annotation provided by the Camel CDI test module, e.g.:

Code Block
languagejava
@RunWith(CamelCdiRunner.class)
@Beans(alternatives = AlternativeBean.class)
public class CamelCdiTest {

    @Test
    public void testAlternativeBean(@Uri("direct:in") ProducerTemplate producerreturn ShrinkWrap.create(JavaArchive.class)
        // Camel CDI
        .addPackage(CdiCamelExtension.class.getPackage())
        // Test classes
        .addPackage(Application.class.getPackage())
        // Bean archive deployment descriptor
        .addAsManifestResource(
            new StringAsset(
   @Uri("mock:out") MockEndpoint mock) throws InterruptedException {
        mockDescriptors.expectedMessageCountcreate(1BeansDescriptor.class);
        mock.expectedBodiesReceived("test with alternative bean!");

            producer.sendBodygetOrCreateAlternatives("test");

        MockEndpoint.assertIsSatisfied(1L, TimeUnit.SECONDS, mock);
       }

    static class TestRoute extends RouteBuilder {

 .stereotype(MockAlternative.class.getName()).up()
          @Override
        public void configure.exportAsString()) {
,
                from("direct:out").routeId("test").to("mock:out");
  "beans.xml");
      }

    }//...
}

Camel context

...

customization

You may need to customise customize your Camel contexts for testing purpose, for example disabling JMX management to avoid TCP port allocation conflict. You can do that by declaring a custom Camel context bean in your test class, e.g.:

...

It is recommended to only advice routes which are not started already. To meet that requirement, you can use the CamelContextStartingEvent event by declaring an observer method in which you use adviceWith to add a mock endpoint at the end of your Camel route, e.g.:

Code Block
languagejava
@RunWith(CamelCdiRunner.class)
public class CamelCdiTest {
 
    void advice(@Observes CamelContextStartingEvent event,
                @Uri("mock:test") MockEndpoint messages,
                ModelCamelContext context) throws Exception {

        context.getRouteDefinition("route")
            .adviceWith(context, new AdviceWithRouteBuilder() {
                @Override
                public void configure() {
                    weaveAddLast().to("mock:test");
                }
            });
    }
}

JUnit rules

Camel CDI test starts the CDI container after all the JUnit class rules have executed.

...