Camel Test
As a simple alternative to using Spring Testing or Guice the camel-test module was introduced into the Camel 2.0 trunk so you can perform powerful Testing of your Enterprise Integration Patterns easily.
| The camel-test JAR is using JUnit. There is an alternative camel-testng JAR (Camel 2.8 onwards) using the TestNG test framework. |
Adding to your pom.xml
To get started using Camel Test you will need to add an entry to your pom.xml
JUnit
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-test</artifactId>
<version>${camel-version}</version>
<scope>test</scope>
</dependency>
TestNG
Available as of Camel 2.8
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-testng</artifactId>
<version>${camel-version}</version>
<scope>test</scope>
</dependency>
You might also want to add slf4j and log4j to ensure nice logging messages (and maybe adding a log4j.properties file into your src/test/resources directory).
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>test</scope>
</dependency>
Writing your test
You firstly need to derive from the class CamelTestSupport and typically you will need to override the createRouteBuilder() method to create routes to be tested.
Here is an example.
public class FilterTest extends CamelTestSupport {
@EndpointInject(uri = "mock:result")
protected MockEndpoint resultEndpoint;
@Produce(uri = "direct:start")
protected ProducerTemplate template;
public void testSendMatchingMessage() throws Exception {
String expectedBody = "<matched/>";
resultEndpoint.expectedBodiesReceived(expectedBody);
template.sendBodyAndHeader(expectedBody, "foo", "bar");
resultEndpoint.assertIsSatisfied();
}
public void testSendNotMatchingMessage() throws Exception {
resultEndpoint.expectedMessageCount(0);
template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
resultEndpoint.assertIsSatisfied();
}
@Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
public void configure() {
from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
}
};
}
}
Notice how you can use the various Camel binding and injection annotations to inject individual Endpoint objects - particularly the Mock endpoints which are very useful for Testing. Also you can inject producer objects such as ProducerTemplate or some application code interface for sending messages or invoking services.
JNDI
Camel uses a Registry to allow you to configure Component or Endpoint instances or Beans used in your routes. If you are not using Spring or OSGi then JNDI is used as the default registry implementation.
So you will also need to create a jndi.properties file in your src/test/resources directory so that there is a default registry available to initialise the CamelContext.
Here is an example jndi.properties file
java.naming.factory.initial = org.apache.camel.util.jndi.CamelInitialContextFactory
Dynamically assigning ports
Available as of Camel 2.7
Tests that use port numbers will fail if that port is already on use. AvailablePortFinder provides methods for finding unused port numbers at runtime.
int port1 = AvailablePortFinder.getNextAvailable();
/*
* Get another port. Note that just getting a port number does not reserve it so
* we look starting one past the last port number we got.
*/
int port2 = AvailablePortFinder.getNextAvailable(port1 + 1);
Setup CamelContext once per class, or per every test method
Available as of Camel 2.8
The Camel Test kit will by default setup and shutdown CamelContext per every test method in your test class. So for example if you have 3 test methods, then CamelContext is started and shutdown after each test, that is 3 times.
| TestNG This feature is also supported in camel-testng |
You may want to do this once, to share the CamelContext between test methods, to speedup unit testing. This requires to use JUnit 4! In your unit test method you have to extend the org.apache.camel.test.junit4.CamelTestSupport or the org.apache.camel.test.junit4.CamelSpringTestSupport test class and override the isCreateCamelContextPerClass method and return true as shown in the following example:
public class FilterCreateCamelContextPerClassTest extends CamelTestSupport {
@EndpointInject(uri = "mock:result")
protected MockEndpoint resultEndpoint;
@Produce(uri = "direct:start")
protected ProducerTemplate template;
@Override
public boolean isCreateCamelContextPerClass() {
return true;
}
@Test
public void testSendMatchingMessage() throws Exception {
String expectedBody = "<matched/>";
resultEndpoint.expectedBodiesReceived(expectedBody);
template.sendBodyAndHeader(expectedBody, "foo", "bar");
resultEndpoint.assertIsSatisfied();
}
@Test
public void testSendNotMatchingMessage() throws Exception {
resultEndpoint.expectedMessageCount(0);
template.sendBodyAndHeader("<notMatched/>", "foo", "notMatchedHeaderValue");
resultEndpoint.assertIsSatisfied();
}
@Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
public void configure() {
from("direct:start").filter(header("foo").isEqualTo("bar")).to("mock:result");
}
};
}
}
See Also