Saturday, January 23, 2016

Exploring CQRS with Axon Framework: Overview of the Testing infrastructure

After a long break, I am finally getting around to picking up on the series: exploring CQRS with Axon Framwork. This post, which is on testing, is one of the two remaining posts left to wrap up the series. The aim is to follow it up, as quickly as possible, with the concluding post.

If you have not been following the series, I would advice to give the other posts a read before continuing with this post. You can start here: Exploring CQRS Architecture with Axon Framework: Introduction

As already mentioned, this post looks at the infrastructure the Axon Framework provides for testing. Just like the other posts in the series, it is accompanied by the exploringCQRSwithAxon project on Github. The Application is a trivial one, meant to further illustrates the content of the posts.


To have the project in the state that illustrates the subject of matter in this post:

first clone the repo:
git clone git@github.com:dadepo/exploringCQRSwithAxon.git

then check out to the appropriate commit hash to have the project in a state that illustrates the topics covered in this post:
git checkout 3edcce7e8a8d0238d934b46e835902f78753bd2d.

Objective of this post

Software testing helps to confirm the “correctness” of an application. It is a crucial and recommended part of the application development process. This post illustrates how to make use of the testing infrastructure available when using the AxonFramework to build a CQRS application.

Testing usual works by executing a method and then we verify if the method resulted in an expected behaviour. This is generally done via two ways:

  1. Confirm that the method results into the expected state changes or
  2. Confirm that the method results into the expected interaction with its various dependencies.

The testing infrastructure provided by the Axon Framework makes use of the first approach.

Instead of calling methods, the test procedure involves dispatching commands into our application and we confirm its “correctness” if the corresponding domain events that emanates are the ones expected.

Axon Framework Testing Infrastructure

The Axon Framework provides the infrastructure that enables the setting up of an application, apply commands to it, and verify that the expected domain events are published.

This approach follows the given, when, then approach we see in Behavioural Driven Testing. where we can set the initial application state [The Given], apply the commands  [The When], and then confirm it leads to the broadcasting of required domain events [The Then].

Axon Framework provides tests fixtures that allows us to apply the when,given, then approach to testing.

The ExploringAxonApplicationTests class in the accompanying application has been updated to make use of this fixture.

If you check the @Before setUp method, you find this:

@Before
public void setUp() {
 fixture = Fixtures.newGivenWhenThenFixture(Account.class);

 fixture.registerAnnotatedCommandHandler
   (new CreditAccountHandler(fixture.getRepository()))
   .registerAnnotatedCommandHandler
   (new DebitAccountHandler(fixture.getRepository()));

 accountNo = "test-acc";
}

Which is basically where we get a hold of the fixture, use the registerAnnotatedCommandHandler method to register the command handling components that we will be sending commands against in our tests.

The testing fixture makes sure any Axon infrastructures needed in the application: command bus, event bus etc are properly set up.

What follows are some of the tests that has been added which confirms certain behaviour in our application. For example in the following test:

@Test
public void testCreditingDebitingAndCrediting() {
 fixture.given(new AccountCreatedEvent(accountNo),
            new AccountCreditedEvent(accountNo, 100.00, 0.0),
            new AccountDebitedEvent(accountNo, 40.00, 100.0))
       .when(new CreditAccountCommand(accountNo, 40.00))
       .expectEvents(new AccountCreditedEvent(accountNo, 40.00, 60.00));
}

we used the given() method to apply three events that should have taken place in the past (AccountCreatedEvent, AccountCreditedEvent and AccountDebitedEvent) then we send in the CreditAccountCommand using the when() method, and finally we used expectEvents() to confirm if the application behaved the way intended, that it, if it produced the expected AcountCreditedEvent domain event.

We also have another test, that confirms the business logic that rejects crediting operation if the amount is greater than a million:

@Test
public void cannotCreditWithAMoreThanMillion() {
 fixture.given(new AccountCreatedEvent(accountNo))
       .when(new CreditAccountCommand(accountNo, 10000000.00))
       .expectException(IllegalArgumentException.class);
}

One thing to note is the fact that the Fixture only works with Aggregates that can be reconstructed from events, thus it can only be used with Aggregates that extends the EventSourcedAggregateRoot interface. Meaning if your aggregate root extends AbstractAggregateRoot, then you would have to test the application whichever way you normally approach testing.

The Axon testing infrastructure is pretty handy and it would probably cover all of your testing requirements.

A cool thing about the testing infrastructure is how it allows the treating a system as a black box, with its public API defined by its commands and events; thus making it easier to refactor internal implementations.

One of the shortcomings (and my personal gripes) with mocked based testing is how it tightly couples API implementations with the unit tests used to test them, such that, if an internal implementation is refactored (with no change in the expected behaviour i.e, the API contract is not broken) you will still end up having to update the code that tests it (since there is the possibility you removed mocks/introduced new ones or you exercise the mocks in a different manner). The Axon testing infrastructure frees the developer of such shortcomings since what determines the correctness of the system is the events published in reaction to commands sent to the system. The internal implementation can easily be changed and as long as the given commands produce the expected events, then you can be rest assured the API contract is still intact.

Next in the series is the concluding post; Exploring CQRS with Axon Framework: Closing thoughts. It will be a summary of my views on CQRS as an architecture and how effectively Axon performs as a framework that helps in fulfilling this architecture.

No comments: