Testing with Apache Camel

Updated:

Today I want to talk about testing. (Crowd runs away screaming)

It’s a topic that comes up pretty often, but it’s not so easy with Camel. Or, should I say, it’s just quite different with Camel.

The approach sometimes seems to be to write tests that are a bit pointless, or just write none at all. Neither approach is good!

How to write great tests

When you write tests, you want to make sure they’re useful to you and the future maintainers of the code that you write today.

Let’s remember that:

  • Unit tests should test a contract – in Camel terms, that probably means testing everything between the “edges” of your routes
  • Unit tests should run in isolation – that is, the outcome of one test shouldn’t affect another. Or, to put it another way, running your tests in a different order shouldn’t have any impact on their success.
  • Unit tests should be maintained as your code evolves – once you write a test, make sure you maintain it.

With that in mind, here’s a runthrough of some of the features of the Camel Test Framework that you can use to help you speed through this stuff.

Assertions and expectations

First identify a MockEndpoint that you will be observing.

End your test case with an assertion, like one of these:

// Assert the endpoint is satisfied within 5 seconds
assertIsSatisfied(5L, TimeUnit.SECONDS, myMockEndpoint);

// 

Sending data to endpoints

Sending data to an endpoint from a file:

template.sendBody("direct:start", new File("src/test/data/chickens.xml"));

Camel testing checklist

I thought it was worth sharing my own personal approach towards writing tests in Camel:

  • Divide complex routes down into smaller ones, and join them together, using Direct endpoints or similar glue. This makes it easier for you to test just your business logic.

    For example - with the routes below, we can just test the processCustomer route in isolation, without having to worry about the web service call, or the File component:

    from("direct:start")
        .split()
        // Call our Direct endpoint here
        .to("direct:processCustomer")
        .to("http://example.com/webservice")
        .to("file:customers/processed");
    
    // Now we can more easily test this route
    from("direct:processCustomer")
        .bean(ProcessCustomer.class)
        .bean(SaveCustomer.class);
    
  • Replace endpoints with mocks - mock endpoints in Camel really give you superpowers. They come with all sorts of methods to test whether a message has arrived, whether it matches certain criteria, whether messages have arrived in the right order… You could write all these tests yourself, or you could just use mocks. Seriously, just use mocks.

    This quick example shows what’s possible using just a small portion of the API. It’s easy to read, and very quick to write:

    MockEndpoint mock = getMockEndpoint("mock:result");
    mock.expectedMessageCount(1);
    mock.expectedBodiesReceived("Hello, world!");
      
    // Verify that the right message arrived within 60 seconds
    assertMockEndpointsSatisfied(60, TimeUnit.SECONDS);
    

    And how do you put mocks in your routes without rewriting your actual route code? Well…

  • Make your endpoints configurable - instead of hard-coding endpoint URIs in your routes, use placeholders. This gives you so much more flexibility to be able to replace your endpoints with anything you like at testing time. This is an ideal way of throwing mock endpoints into your routes.

    If you’re using Spring Boot, this is very easy. For example, in the route below, I’ve made the from and to endpoints as placeholders:

    from("{{uri.from}}")
        .bean(MyCustomBean.class)
        .to("{{uri.to}}");
    

    Then, in your test class, you can replace these with whatever endpoints you like:

    @SpringBootTest(classes = YourApplication.class, properties = {
        "uri.from = direct:start",
        "uri.to   = mock:output" })
    
  • Test your logic, not endpoints - write tests to verify that your logic works as expected – for example, that a customer is flagged correctly based on their loyalty status, or that your order validation works. Probably don’t test that you can send an email or you can move files from one place to another.

  • Write an additional test whenever you fix a bug or find an edge case - use bug fixing as an opportunity to write a new test, so that the same bug cannot be reintroduced.

    @Test
    public void testCustomerWithNoAddressIsRejected() {
      //...write stuff here
    }
    
  • Don’t write tests that solely test Camel components - for example, we already know that Camel’s File component can write files. This doesn’t need to be tested again by you. :) Better to focus on testing your own business logic.

Have you got your own approach to testing Camel routes? Share it in the comments!

Leave a Comment

You can use Markdown in your comment. To write code, indent lines by 4 spaces.