So you’ve implemented a beautiful little integration application and it’s ready to be deployed in a test environment of some sort. But you started to ponder about quality: what can you as a developer do about it? Well, you’re on the right track since when professionally developing integrations, proper testing, and quality assurance are as important as in any development project. So as an integration developer, you should be aware that your application is not truly ready for the next stage before there are unit tests.
What are these unit tests then? Essentially they are pieces of software that run and ensure the correct functionality of other pieces of software. So we implement some nifty new functionality which quite possibly is a part of a much bigger, more complex software and then we develop some unit tests which emulate the conditions surrounding the newborn functionality and run the functionality to see if it works as expected. At this point, it’s not necessarily wise to limit the tests only to the magical winter wonderland scenario where everything goes smoothly but to also develop tests that cause something to malfunction inside the functionality and see whether the software recovers from those in an expected way.
Some developers do this the other way by implementing the tests first and then implement the functionality satisfying the tests. Whichever way we elect to go, only after both the functionality and tests are implemented and working properly, is our application ready to advance to the next stage of development.
Generally, these tests should continue to serve a purpose in the grander scheme of things after they have left the developer’s local environment. When an application is deployed to any environment by a build pipeline, the pipeline should be configured so that the unit tests are run automatically after the application code is compiled. If any of those tests fail, the compiled application should not be deployed to the target environment.
Unit testing is of course a far more complex subject, but in my view, it’s most important to remember that implementing unit tests is an integral part of the implementation process and thus a responsibility of the developer. But it’s not only a responsibility – unit testing is an immensely useful tool for developers: when familiarising myself with a pre-existing application, I generally start by looking at the unit tests to get an idea of what the application tries to do.
Unit Testing in Camel Integrations
In the simplest manner integration is systems communicating with each other: integration application receives information from one system and sends it to another. So unit tests should do their best to ensure that the information received is sent in the desired manner.
Let’s start the implementation with assumptions: the data is received by REST and the integration passes it on by REST. One other assumption is that our choice of technology is Apache Camel.
I always start with the happy case test scenario where everything works as it should because generally, other tests are easy to iterate from that. In this case, it would be advisable to mock the REST endpoint where the integration is sending the information. To mock something is to make it appear like a real thing to an interested party – in this case, we’re making an endpoint that appears genuine to the integration application, but in reality, only our test functionality receives the sent information. Using Camel’s built-in test support framework this is quite easily achieved. So we configure the mock endpoint for the test and add a response to it as an answer to the REST call. Then in the test, we call the integration application’s receiving endpoint using our favorite HTTP library and just wait for the mock endpoint to receive the call. The last thing in the test is to ensure that the mock endpoint received the same information as the REST call indicated and our test received the matching answer to what we configured to the mock endpoint.
As stated error scenarios could be iterations from the happy case scenario. We want to ensure that the integration application reacts in an expected way when the receiving system is unresponsive when received information is malformed when the receiving system responds with malformed information etc. Generally, the use case dictates what kind of error cases or other happy cases is to be tested, but my experience says that when more cases are covered by unit tests, less time is wasted in proper testing phases.
However, the integration is almost always more complicated than our example. Most likely the systems do not support the same message format so transformations are needed, and there should be unit tests for ensuring those transformations work as expected. Further down the line, the integration might be needed to enrich and/or consolidate the information from third systems or notify other systems based on the information content. Maybe our integration application needs to asynchronously communicate with one of the systems. These all should be covered by unit tests which means more complex test cases and more mocked endpoints.
When implementing testing for the more complex integrations it’s important to remember that our individual test cases do not need to be catch-all. As Apache Camel is a route-based framework in which solutions generally consist of more than one route, every route could be unit tested separately. In most cases, that kind of approach could be somewhat counterintuitive, but route mocking can be used to achieve some streamlining in the test cases. For example, when we need to enrich our information from a third system, instead of mocking the third system’s information fetching endpoint, we mock the Apache Camel route which is responsible for the said fetching, and implement its own tests for the fetching route.
Case: Unit Testing in AD Integration
Just recently I had an opportunity to work with the Apache Camel application which is used to integrate user operations with Microsoft Active Directory. I’ll illustrate a couple of my previous points with this integration, but I’m going to only focus on the user update process of the application, and more specifically the unit testing of that process.
The process is straightforward enough although it contains several calls to the external system – Active Directory – for data enrichments and checks in addition to the actual update request. Luckily Apache Camel’s extensive library of components contains one for relatively easy communication with Active Directory, and for testing purposes, there’s ApacheDS – an open-source directory server.
In integration unit testing there’s sometimes a need to simulate external systems which we are communicating with if it’s feasible and offers something of value to the tests. In this case, ApacheDS offers us a way to easily ensure the specific data structures and connections to an actual directory server. For example, to test a user creation process we start the ApacheDS, create a user with the application, and then check ApacheDS for the newly created user.
In the case of a user update process, we start by configuring the ApacheDS with users and groups: especially the user we want to update and groups that the user might belong to. Then we create an update message for that user and send it to our integration application’s user update interface and check the ApacheDS if the user is updated. In error cases, we might want to ensure everything goes in an expected manner when there’s no matching user to be updated or the update message tries to update some value that is defined as not updatable by this process.
Unfortunately, ApacheDS is not as primed on the user group side as we would like it to be so now would be the perfect time to remember that we are testing the integration process in the Apache Camel application, not ApacheDS. Keeping that goal in mind we should mock the group operations of the update user process – in fact, we mock the whole routes that are used in the application to communicate with the directory.
On the technical side of things, the user information is updated on the ApacheDS, and in the test we ensure that from the ApacheDS in a normal manner. After that, we receive group queries in the mock endpoint and we ensure that data in those queries is as expected and set according to responses to the mock endpoints. At the end of the test basically the same is done to the final group data with checks and responses and all.
As stated before this is to ensure that the main route for processing the user update data works so to achieve clear results we mock routes that are subsidiary to this particular process. Of course we proceed to test those mocked routes in other test cases to ensure the wholesale robustness of the application.
Unit testing with Apache Camel goes over and beyond this and approaches vary depending on the cases we face. Hopefully this blog offers some insights into how to tackle unit testing for even somewhat more complex integrations.