Testing Apache Camel 3.13.x in SpringBoot application in examples

Vyacheslav Boyko
5 min readNov 23, 2021

--

Apache Camel is a framework for data transformation and delivery.

In a few words:

  1. Apache Camel gives you a DSL (Domain Specific Language) that helps you to describe how the data should be transformed.
  2. Apache Camel has over 330 different connectors, by means of which you could easily gather your data and deliver it wherever it needs.
  3. Apache Camel provides you a mechanism of thread manipulation under the hood to process any job in parallel.

So…

Consider I have a folder. And text files are being collected in this folder by the other application.

My task is to check every file on bad words (e.g. duck) to replace it with a good one (e.g. ostrich).

You could think about fetching each file to process it line-by-line superseding each bother word with permitted one.

Schema describing data transformation of superseding bad words with good ones in every incoming file

You are correct. I’ve implemented it for you in Camel. Take a look.

Spring Boot bean to implement Camel Route — I like such charm. By the way you should not write such comments.

How to test this code? - you may ask. And the answer is — let’s write a test.

After reading official documentation about how to test Apache Camel applications I need to add all needed dependencies (consider of using maven or gradle).

In gradle:

testImplementation 
'org.apache.camel:camel-test-spring-junit5:3.13.0'

N.B.! When writing a test covering Apache Camel Route it should not test the side of work that Apache Camel does. It should not test how Camel connects to AWS or sends email — all such stuff is tested already in Apache Camel sources. You should implement a test covering only functionalities instead.

According to this rule I should not listen to new files in a folder, should not write the result into another folder. All I should do — is to mock my IN and OUT folders and test if my data transformation is correct.

In order to mock my from- and to-endpoints I’m going to remove folders names explicitly specified into endpoints definition and put them in another Spring Bean — property provider. It will be straightforward to substitute folders with mocks in my test.

I am to construct an interface providing input and output endpoints and the class implementing it.

notice this class is not for tests

And another property provider for test with mocked endpoints.

this class is for tests only — it is placed into src/test/java path

So my test is below.

Notice the following:

  1. Test is annotated with @SpringBootTest and@CamelSpringBootTest both
  2. @SpringBootTest has classes property specified with Application class
  3. In test method mocked endpoint is adjusted with several expectations
  4. After a message is sent into from-endpoint the mocked endpoint checks all caught messages on expectations to be satisfied

So when you run this application with options
-Dapp.input.folder=/tmp/in -Dapp.output.folder=/tmp/out
-Dapp.bad-word=duck -Dapp.good-word=ostrich

creating two folders /tmp/in and /tmp/out beforehand, every file placed into /tmp/in is to be processed and every ‘duck’ becomes ‘ostrich’ now. Let’s see it.

$ echo "123 duck
456 luck
789 duck tales" > /tmp/in/test
incoming file has been processed
$ cat /tmp/out/test
123 ostrich
456 luck
689 ostrich tales

Now consider I’m interested in supervision each file with external Supervisor over HTTP. The Camel Route becomes the following.

My test becomes red as expected.

it needs to add dependency for HTTP Camel component, but it is not necessary for testing

How do I deal with it now? What is if external service for supervising is not available from CI/CD where tests to be run? What is if this external service is not available even from my laptop? The answer is — to disable external service call in test without changing route definition.

I am to move external endpoint definition into Property Provider to segregate production and test code.

segregating production and test external service — mock it in test
route definition use endpoint definition provided by property provider

Ok, how about my test?

Apache Camel Test KIT provides a tool called Advice which helps you to mock dealing with external services. You can “advice” Camel to (e.g.) set a concrete value into body instead of calling external service. The test becomes as following.

I advice Camel to set a concrete body instead of calling supervisor service

But the only thing you should remember is how to enable this Advice mechanism. It is enabled with annotation @UseAdviceWith on Test class.

enabling Advice mechanism

By enabling Advice mechanism it disables automatically Camel Context starting. It leads you to enable it manually in test.

camelContext.start(); // start CamelContext explicitly
// perform test - send a message into route
camelContext.stop(); // stop CamelContext explicitly

The debug log shows the use of advice.

notice that Camel Context is not started automatically

A debug log of test is the following.

For tests taking good sense it is necessary to implement another case — if supervisor tells to skip a file. I set mock endpoint is expected to receive 0 messages (0 files expected to move into /tmp/out folder). And I set the Camel Advice to put “SKIP” as the response of supervisor.

This test is green, be sure.

You can find my sources on GitHub.

I hope my story helps you to test your Camel routes better and more straightforward.

--

--

Vyacheslav Boyko

Software developer, writing in Java. Over 15 years in IT. Enthusiast in EDA, Microservices, Integrations, experiencing in finance, trading, banking. He/Him.