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.
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
.
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>
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:
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"; }
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;
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:
getBlueprintDescriptor()
before each test@Produce
and @EndpointInject
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.
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.
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 }; }
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.
3.137.172.115