Testing routes defined in OSGi Blueprint

This recipe expands on the core testing capabilities described so far by detailing the steps needed to test Camel routes defined using the XML DSL in an OSGi Blueprint application. You will learn how to assemble a test harness that replaces parts of the application in order to test your routes outside an OSGi deployment environment, including the substitution of OSGI Configuration Admin service ${..} placeholders with test values. Camel allows Blueprint-based routes to be tested outside of an OSGI container, through a project called PojoSR that makes it possible to test the code without deploying it.

Getting ready

To use Camel's Blueprint test support, you need to add a dependency for the camel-test-blueprint library that provides the support classes for JUnit testing of Blueprint as well as a transitive dependency on JUnit itself.

Add the following to the dependencies section of your Maven POM:

<dependency>
  <groupId>org.apache.camel</groupId>
  <artifactId>camel-test-blueprint</artifactId>
  <version>${camel-version}</version>
  <scope>test</scope>
</dependency>

The Java code for this recipe is located in the org.camelcookbook.examples.testing.blueprint package. The Blueprint XML files are located under src/main/resources/OSGI-INF/blueprint.

How to do it...

Consider the following route, defined in simpleTransform-context.xml:

<blueprint
    xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
      http://www.osgi.org/xmlns/blueprint/v1.0.0
        http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
      http://camel.apache.org/schema/blueprint
        http://camel.apache.org/schema/blueprint/
        camel-blueprint.xsd">
  <camelContext xmlns="http://camel.apache.org/schema/blueprint">
    <route>
      <from uri="direct:in"/>
      <transform>
        <simple>{{transform.message}}: ${body}</simple>
      </transform>
      <to uri="mock:out"/>
    </route>
  </camelContext>
</blueprint>

Here, we use a placeholder to fetch the transform message. We define an OSGi Configuration Admin property placeholder in a file alongside it, located in simpleTransform-properties-context.xml (namespace declarations partially shown):

<blueprint ...
    xmlns:cm="http://aries.apache.org/blueprint/xmlns/
              blueprint-cm/v1.0.0"
    xsi:schemaLocation="...
      http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0
        http://aries.apache.org/schemas/blueprint-cm/
        blueprint-cm-1.0.0.xsd">
  <cm:property-placeholder 
      persistent-id="org.camelcookbook.testing">
    <cm:default-properties>
      <cm:property name="transform.message"
                   value="Modified"/>
    </cm:default-properties>
  </cm:property-placeholder>
</blueprint>

Tip

It is a good practice to place any beans that have complex dependencies, or could be considered services, in a separate configuration file from your routes. This makes it easy to provide an alternative version of those beans in your test by providing a different version of that file to the test runtime.

To test this route, perform the following steps:

  1. Create a test class that extends org.apache.camel.test.blueprint.CamelBlueprintTestSupport:
    public class SimpleTransformBlueprintTest
        extends CamelBlueprintTestSupport {
      //...
    }

    The CamelBlueprintTestSupport class is an abstract class that is responsible for instantiating the Camel context with the routes under test, creating utility objects, and setting up a simulated OSGi environment. To test a route, override the getBlueprintDescriptor() method, and return the Blueprint configuration files that contain the Camel routes under test, as well as any support beans, as a comma-separated string:

    @Override
    protected String getBlueprintDescriptor() {
      return "/OSGI-INF/blueprint/simpleTransform-context.xml,"
           + "/OSGI-INF/blueprint/simpleTransform-props-context.xml";
    }
  2. Define the body of the test. Here, the MockEndpoint testing DSL is used to establish the messages that you expect an endpoint to receive. A message is sent into the route via the ProducerTemplate instance provided for you by the CamelTestSupport base class. Finally, you assert that the expectations set on the mock endpoint were satisfied:
    @Test
    public void testPayloadIsTransformed() 
        throws InterruptedException {
      MockEndpoint mockOut = getMockEndpoint("mock:out");
      mockOut.setExpectedMessageCount(1);
      mockOut.message(0).body().isEqualTo("Modified: Cheese");
    
      template.sendBody("Cheese");
    
      assertMockEndpointsSatisfied();
    }

    As an alternative to explicitly fetching mocks, and referring to endpoints in each unit test, you can request an injected MockEndpoint and ProducerTemplate by defining them as fields and annotating them as follows:

    @EndpointInject(uri = "mock:out")
    private MockEndpoint mockOut;
    
    @Produce(uri = "direct:in")
    private ProducerTemplate in;

How it works...

Camel's Blueprint test support avoids the need for an OSGi runtime by instantiating the Blueprint context files using the PojoSR library. This library provides a container that supports an OSGi-style service registry without a full-blown OSGi framework. This leads to the container starting and stopping faster, which is more appropriate for unit testing.

The CamelBlueprintTestSupport class is a convenience class that provides feature-parity with the CamelTestSupport class described in the Testing routes defined in Java recipe. It is responsible for performing the boilerplate work required to test Camel routes defined within Blueprint configuration files. At its most basic, the class will do the following:

  • Start a Blueprint application defined by the Blueprint descriptors returned from getBlueprintDescriptor() before each test
  • Inject any properties that you have annotated with @Produce and @EndpointInject
  • Shut down the Blueprint application at the end of each test

Feature-parity with CamelTestSupport means that aside from the implementation of different base methods to the Java testing example (getBlueprintDescriptor() versus createRouteBuilder() or createRouteBuilders()), CamelBlueprintTestSupport allows the test methods themselves to be written in exactly the same manner to their Java DSL equivalents. Both classes provide access to the same protected variables (context and template), and honor the same test lifecycle.

Note

At the time of writing this book the camel-test-blueprint library is not as functionally mature as camel-test-spring. The only test framework that is supported is JUnit 4. There is also no alternative that would enable you to work with other test frameworks as per the Extended Spring Test mechanism described in the Testing routes defined in Spring recipe.

There's more...

The Blueprint Configuration Admin support allows you to define default values for properties. These values are associated with a Persistent ID (pid) that corresponds to a named map, which is typically overridden inside an OSGi environment. When testing, you will typically want to override these values with something more suited to your needs.

A simple solution, if your <cm:property-placeholder/> block is defined in a separate file from your routes, might be to define a test version of that file and return it from getBlueprintDescriptor().

A better approach is to override the properties individually by overriding the following method in your test class:

@Override
protected String useOverridePropertiesWithConfigAdmin(
    Dictionary props) throws Exception {
  props.put("transform.message", "Overridden");
  return "org.camelcookbook.testing"; // Persistent ID
}

If you have a lot of properties that require overriding, or a lot of tests that would require you to repeat this code block, you can provide the location of a properties file (ending in .properties or .cfg) and the Persistent ID to override as follows:

@Override
protected String[] loadConfigAdminConfigurationFile() {
  return new String[] {
    "src/test/resources/blueprint/testProperties.cfg",
    "org.camelcookbook.testing" // Persistent ID
  };
}

Note

The Blueprint test mechanism has been written with Maven in mind, and loads the properties file relative to the project root. The test as specified previously will most likely not run from within your IDE, as the PojoSR container will execute within a different absolute directory.

When this mechanism is combined with useOverridePropertiesWithConfigAdmin(), the properties file will override the default properties provided in the <cm:property-placeholder/> block, and the manually set properties will in turn override the values in the file.

We can use this particular mechanism to make testing easier. Consider the following example:

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  <route>
    <from uri="{{in.endpoint}}"/>
    <transform>
      <simple>{{transform.message}}: ${body}</simple>
    </transform>
    <to uri="{{out.endpoint}}"/>
  </route>
</camelContext>

By externalizing the endpoints we can now exercise the route in a test using direct: and mock:, while referring to the actual technology endpoints in the production configuration.

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.172.115