Unit Testing Source Code: Verifying Axon Event Upcaster Consistency

In our last project we used a JUnit test to
check consistency of all code concerned with Axon event upcasting. We think
this is interesting as we basically unit test source code rather than class
behavior. In this blog we describe how we implemented it and conclude with a
short evaluation and we list alternative approaches.

Source code consistency

The system we were building is using the
Axon framework (Axon
Framework) and in Axon all events in the system end up in an event
store to be used for event sourcing later. We for example defined “Persons” and
“Organisations” where Persons work for Organisations. During development such entities
change for example, they get a new
property. Events such as “OrganisationUpdated” therefore become versioned. When new versions, in Axon
terms revision, contain mandatory new
properties we have older events in our event store without those properties. In
Axon terminology they need to be upcasted
by Java classes called Upcasters. Over time there will be several upcasters
taking an event first from revision 0 to revision 1, then from 1 to 2 and so on
until we reach the current one.

In our project we introduced a generic way
of managing upcasters and we introduced a coding standard for it as follows. We
have an event in Kotlin defining the current revision like this:

We need this ordering because Axon runs all
events through a list of all upcasters it knows about at event replay time.
Axon does not impose anything in terms of ordering that list but we want to
apply upcaster “…NullTo1” before “…1To2” etc. Managing Axon upcasters over time
will be another blog post to be written soon as there are a couple of
alternative implementations all with different pro’s and con’s.

Let us get back to our current subject. To
summarize, in our coding standard we have:

The current revision in an annotation
on an event (e.g. “@Revision(“4”)”)

A series of classes with a
particular name (e.g. …NullTo1, …1To2, ….)

Per upcaster an INPUT and
OUTPUT revision (e.g. “1” and “2”)

The previous revision in an annotation
on an upcaster (e.g. @Order(1))

Given an event with a Revision “4” this
implies:

There should be 4 upcasters

The name of each upcaster (OrganisationUpdatedEventUpcaster1To2) contains 2 revisions that should match values specified in INPUT (1) and OUTPUT (2) constants

Likewise, the name of each upcaster
(OrganisationUpdatedEventUpcaster1To2) contains the TypeName used in INPUT and OUTPUT (OrganisationUpdatedEvent)

The value in the “@Order”
annotation should equal the revision used in the INPUT(1)

Now how do we know that this standard is properly
implemented? A JUnit test would add value to catch mistakes that are easily
made and easily overlooked at review time such as copy-paste errors:

Forgetting to change the type
in INPUT and OUTPUT constants

Forgetting to update the @Order
annotation

And other errors like omitting the upcaster
in the first place.

The JUnit Test

The test verifying the implementation of our
coding standard relies on reflection but that should not be a surprise. Our
first job is to obtain all event classes. As all events are processed by methods
annotated by either “EventHandler” or “EventSourcingHandler” Axon annotations,
we get all those methods and then infer their input parameter using code like
this:

Getting the current revision is relatively
straightforward as it is a “Revision” Axon annotation on the event. We can do
this by getting the annotation on the class and then getting the value out like
this:

Note we had to change both accessibility as
well as the modifiers to get to the values we were after. If you want to use
these techniques please do mind that not all java Security Managers will allow
this, the default one for java 11, however, does although it gives a warning
about modifying accessibility on the modifiers field.

Why did we build this test in the first place and what was
the result?

One moment in the project we decided to add
the ID of the current user to all events to enable full auditability. We
already had thousands of events in the event store and decided that the current
user’s ID would be mandatory. So we had
to write about 40 upcasters. Some of the events already had a revision so the
addition was relatively repetitive. After a while we wondered if we did not
make any copy-paste error and started to think about an automated (and
repeatable) test. And it actually caught an error albeit just one: we forgot to
change the TypeName in INPUT and OUTPUT constants of an upcaster that was
copy-pasted.

Also worthwhile mentioning is that in an
earlier stage of the project we suffered from a bug that would have been caught
by our test. And finally our test has caught yet another error in a recent
stage.

In general we believe our approach is
worthwhile when introducing a new field in many places on behalf of a cross
cutting concern.

Concluding

We have seen how consistent usage of a coding standard for Axon upcasters can be verified by using reflection in a JUnit test. Not all coding standards can be verified by using only reflection, of course. Tools like Sonar for instance are specifically built for this purpose. Trying to build Sonar rules for the use case presented above is therefor the topic of our next blog.