Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Instant Apache Camel Message Routing

You're reading from   Instant Apache Camel Message Routing Route, transform, split, multicast messages, and do much more with Camel

Arrow left icon
Product type Paperback
Published in Aug 2013
Publisher Packt
ISBN-13 9781783283477
Length 62 pages
Edition 1st Edition
Arrow right icon
Author (1):
Arrow left icon
Bilgin Ibryam Bilgin Ibryam
Author Profile Icon Bilgin Ibryam
Bilgin Ibryam
Arrow right icon
View More author details
Toc

Testing the messaging applications (Advanced)


Integration applications are asynchronous, heterogeneous, and message driven in nature. Traditionally testing such applications is challenging, especially when there is not good tooling support. As a consequence, most of the testing is done manually at the end of the project with or without very little automated tests. Fortunately, Camel offers a variety of helper tools and makes writing routing tests a pleasurable activity. It can run routes isolated in a test container, mock external systems, specify expectations, trigger events, match expectations, simulate load or certain behavior, and so on. Let's see how to test a route written in Java DSL and then the additional tools and techniques used with Camel.

Getting ready

The complete source code for this tutorial is located under the following project: camel-message-routing-examples/testing-routes.

We will use JUnit, but there is also TestNG support, although it has fewer features.

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit-version}</version>
    <scope>test</scope>
</dependency>

For testing applications using Java DSL we need the camel-test dependency:

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

How to do it...

  1. Let's assume that we want to test the following route defined in Java DSL:

    public class SimpleChoiceRoute extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        from("direct:start")
            .choice()
                .when(body().isEqualTo("orange"))
                    .to("mock:oranges")
                .when(body().isEqualTo("apple"))
                    .to("mock:apples");}
    }
  2. To test this route we have to extend the abstract CamelTestSupport class in our test and instantiate the route we want to test in the createRouteBuilder method:

    public class SimpleChoiceRouteTest extends CamelTestSupport {
        @Override
        protected RouteBuilder createRouteBuilder() throws Exception {
            return new SimpleChoiceRoute();
        }
    }
  3. For each test, CamelTestSupport will create a CamelContext, run our route under test, and then tear everything down at the end of the test. Let's add our first test:

    @Test
    public void sendsAnAppleMessage() throws Exception {
        MockEndpoint mockOranges = getMockEndpoint("mock:oranges");
        MockEndpoint mockApples = getMockEndpoint("mock:apples");
        mockOranges.setExpectedMessageCount(0);
        mockApples.setExpectedMessageCount(1);
    
        template.sendBody("direct:start", "apple");
        mockOranges.assertIsSatisfied();
        mockApples.assertIsSatisfied();
    }
  4. The test method first gets hold of the mock endpoint and sets the expected number of messages, then using producerTemplate triggers an event and finally verifies that the expectation is matched. This is the general expect-run-verify test pattern. To make the test more readable, we could do a little bit of refactoring. For example, use annotations and declare MockEndpoints as instance variables, specify default Endpoint for ProducerTemplate, assert all mock endpoints in once step, and so on:

    @Produce(uri = "direct:start")
    protected ProducerTemplate start;
    
    @EndpointInject(uri = "mock:oranges")
    private MockEndpoint mockOranges;
    
    @EndpointInject(uri = "mock:apples")
    private MockEndpoint mockApples;
    
    @Test
    public void orderSomeFruits() throws Exception {
        mockOranges.expectedBodiesReceived("orange");
        mockApples.expectedBodiesReceived("apple");
    
        start.sendBody("orange");
        start.sendBody("apple");
        assertMockEndpointsSatisfied();
    }

How it works...

CamelTestSupport is the base class for testing routes. During the setup stage, it will create a new CamelContext, producerTemplate, consumerTemplate, load the routes defined in the test class and start them. Then, in our test, we can access mock endpoints, set expectations and trigger events or directly send messages to the route endpoints using producerTemplate. At the end of each test method CamelTestSupport will stop the routes and CamelContext (unless it is configured to tear down everything at the end of all tests in the test class).

The base class also provides lots of other helpful methods and hooks to customize the test lifecycle. For example, in some cases it is required to set expectations or do some work before starting the routes. That is possible by overriding the isUseAdviceWith method to return true which will prevent CamelContext from starting automatically and it has to be started manually as part of tests:

@Override
public boolean isUseAdviceWith() {
    return true;
}
public void pollsFilesOnStart() throws Exception {
    getMockEndpoint("mock:result").expectedBodiesReceived("Some file content");
    camelContext.start();
    assertMockEndpointsSatisfied();
}

Mock endpoint (http://camel.apache.org/mock.html) is another helpful tool used for testing routes. It is similar to other mocking libraries such as Mockito and jMock, and represents an inmemory list that collects all Exchanges it interacts with. It has mainly two types of methods: for specifying expectations and for verifying them, but it can also simulate specific behavior:

mockReplying.whenAnyExchangeReceived(new Processor() { 
    @Override
    public void process(Exchange exchange) throws Exception {
        Message in = exchange.getIn();
        in.setBody("Mock response: " + in.getBody());
    }
});

The previous code snippet configures mockReplying to modify the message body when an Exchange is received in order to simulate the behavior of the mocked endpoint.

There's more...

Testing Spring XML DSL is no different, apart from using the Spring's ApplicationContext for creating the routes. Here, we will have a look at that and a few other testing tools.

Testing applications written in Spring XML DSL

To test Camel applications written in XML DSL, the camel-test-spring dependency is needed.

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

The main difference to testing Java routes is that, instead of extending CamelTestSupport, the test class has to extend CamelSpringTestSupport and override createApplicationContext, rather than createRouteBuilder:

public class SimpleChoiceRouteSpringTest extends CamelSpringTestSupport {
    @Override
    protected AbstractApplicationContext createApplicationContext() {
        return new ClassPathXmlApplicationContext("META-INF/spring/simple-choice-route-context.xml");
    }
    //the rest of the code is the same as previous
 }

CamelSpringTestSupport provides feature-parity with CamelTestSupport, which means it allows tests to be written in the same style, provides the same protected variables, methods, and honors the same lifecycle. In fact we can use the same instance variables and test method from the previous example to test the Spring XML route.

There is also a way for running tests without extending the CamelSpringTestSupport class, called Enhanced Spring Test Support. In this scenario, the test class has to be annotated with @RunWith(CamelSpringJUnit4ClassRunner.class), and use other annotation to inject Endpoints, CamelContext, and so on, because they are not available as protected fields of the parent class:

@RunWith(CamelSpringJUnit4ClassRunner.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
@ContextConfiguration(locations = {"classpath:/META-INF/spring/simple-choice-route-context.xml"})
public class SimpleChoiceRouteEnhancedTest {
    @Autowired
    private CamelContext camelContext;

    @Produce(uri = "direct:start")
    protected ProducerTemplate start;
    //the rest of the class is the same as the example previous
}

The previous tests are mainly testing the message transformations and the routing logic in Camel routes. If the application interacts with external systems, there is also a need for integration testing to verify the interaction points. To see more testing techniques check the tests for the other examples from this book.

Other tools for testing

  • AdviceWith (http://camel.apache.org/advicewith.html): Sometimes, a route has hardcoded Endpoints which makes it hard to test, or it might be necessary to modify part of the route before testing. For these kinds of scenarios Camel provides an Aspect-Oriented-Programing (AOP) model with the name AdviceWith. This feature makes it possible to modify the routes after they are loaded to CamelContext and provides methods to add, remove, and replace endpoints by name or ID.

  • NotifyBuilder (http://camel.apache.org/notifybuilder.html): This allows testing routes without modifying them by building conditional expressions and then testing or waiting for that condition to occur. Good for integration testing with external endpoints.

  • DataSet (http://camel.apache.org/dataset.html): The DataSet component provides a mechanism for easily performing load and soak testing of the system.

lock icon The rest of the chapter is locked
arrow left Previous Section
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime