Unit Testing Camel - A simple example

In a previous post I wrote about taking a Test-Driven approach to Camel development. I’ve been asked to share the code and explain some of the steps in more detail so here we go…

The code for this article is here

The Route

For this example I’ve created a simple route:

1
2
3
4
5
6
7
8

from("timer://example?repeatCount=1")
.routeId("simple-rest")
.process(this::createMessage)
.marshal().json()
.setHeader(Exchange.CONTENT_TYPE, constant("text/xml; charset=utf-8"))
.to("https://en1gvb5qo7vg4.x.pipedream.net");

This code is triggered from a Timer, the ‘repeatCount’ option specifies that it should fire only once. We then create a message in the createMessage function, convert the message to JSON, set the ‘Content-Type’ header and send the message to an HTTP endpoint. In this case im using a RequestBin.

If we run the application we can see this in the RequestBin:

Mocking the Http Endpoint

To effectively test this route we must be able to mock the HTTP endpoint. To do this we need to add the following annotation to our test class:

1
2
3
4
5

@MockEndpointsAndSkip("https://.*")
public class simpleRestRouteTests {
}

This tells the test framework to create a Mock for any endpoint that starts with ‘https://‘. This Mock can then be accessed like this:

1
2
3
4

@EndpointInject("mock:https:en1gvb5qo7vg4.x.pipedream.net")
private MockEndpoint restEndpoint;

The value passed to EndpointInject must match the endpoint value configured in your route.

A Simple Test

Let’s write a test to ensure that Camel is calling the endpoint.

The first thing we want to do is to replace the Timer component that triggers the flow. This will make it easier to inject data into the Route. To do this we use AdviceWith.

1
2
3
4
5

AdviceWith.adviceWith(camelContext,
"simple-rest",
rb -> rb.replaceFromWith("direct:file:start"));

This swops the ‘From’ in the Route (currently a Timer) with a new Direct component.

Next we can set up an assertion. We want to configure that the HTTP endpoint is called exactly once. We can do this via the Mock we referenced earlier:

1
2
3

restEndpoint.expectedMessageCount(1);

Finally we want to send a message into the route and assert that the Mock was called:

1
2
3
4

producerTemplate.sendBody("direct:file:start", "Hello from Test!");
restEndpoint.assertIsSatisfied();

Here’s the full listing…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

@Test
public void EnsureThatEndpointIsCalled() throws Exception {

AdviceWith.adviceWith(camelContext,
"simple-rest",
rb -> rb.replaceFromWith("direct:file:start"));

camelContext.start();

restEndpoint.expectedMessageCount(1);

restEndpoint.whenAnyExchangeReceived( (Exchange exchange) -> {
logger.log(Level.INFO, "Mock was HIT!!!");
});

producerTemplate.sendBody("direct:file:start", "Hello from Test!");

restEndpoint.assertIsSatisfied();
}

Sending specific results from the Mock

Sometimes we need a Mock to return a specific response body, header or HTTP response code. To set this up we need to use AdviceWith again.

1
2
3
4
5
6
7
8
9
10
11

var HTTPHeader = new SimpleExpression("200");
HTTPHeader.setResultType(Integer.class);

AdviceWith.adviceWith(camelContext,
"simple-rest",
rb -> rb.weaveByToUri("https://en1gvb5qo7vg4.x.pipedream.net")
.replace()
.setHeader("CamelHttpResponseCode", HTTPHeader)
.setBody(new ConstantExpression("Response")));

What’s happening here is that we are replacing the endpoint “https://en1gvb5qo7vg4.x.pipedream.net“ with a new implementation that returns our custom HTTP header and a body of “Response”.

Next Steps

This should be enough to get some basic testing in place. Let me know in the comments if there are any other scenarios you’d like to see covered.

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2015-2024 Nick Mckenzie

请我喝杯咖啡吧~

支付宝
微信