Testing for services

For examples of how to test services, please look at the repository fineract-cn-template.

Unit tests

Unit tests are tests which cover a single class.  Unit tests are kept in the repository module, and the package name of the class they tests.  Most services have three submodules: api, service, and component test.  Unit tests should be written to cover the first two of these submodules.  Unit tests should be very fast.  A unit test which takes a second is already too long.  Unit tests should not access resources like databases or files.


The api module contains very little original executable code.  Instead the api module is mostly made up of interfaces and domain objects.  One unobvious exception to this is the annotations on the domain objects.  These define valid ranges for fields, and valid combinations of field values.  They provide a first simple layer of defense against invalid inputs.  They are checked on the service side, but they can also be checked on the client side.  When the checks fail, output is in a consistent, programmer-readable format to help programmers correct their mistakes.

Unit tests of the api module are located under <service>/api/src/test/.  Most of these unit tests check the validation of domain objects.  They should test happy cases and edge cases.  To do this these tests consist of an initial version of the domain object, and a list of changes to the domain object, plus whether the change should produce a valid or invalid domain object.  For an example see the SampleTest.


The service module contains the bulk of the executable code.  Here unit tests are based on JUnit, and dependencies between classes are replaced using Mockito.  We have chosen not to test all the code in a service this way.  Instead we've leaned heavily on somewhat more holistic component tests.  Good candidates for classes to unit test are:

  • mapping logic for domain objects to database entities,
  • calculation logic

Component tests

Component tests test an api and a service together but mock any services that the service-under-test is dependent on.  Component tests are kept in the same repository with the code they test.  Component tests submit changes via the feign api, listen for changes using ActiveMQ, and check results using the feign api again.  Data for component tests is persisted to an embedded Cassandra and an embedded MariaDB.  These are spun up before the service is spun up.  Because so many things need to be started, starting a test can be rather slow.

running a suite of tests

If the component tests are programmed correctly, multiple tests can be run in one go.  This saves a lot of clicking for the programmer, but it's also faster because the necessary infrastructure is spun up once at the beginning and then spun down at the end.  Not all services have this ability, but those that do have a so-called TestSuite.  All of the component tests for the service are entered in the TestSuite.  A test which is entered in the TestSuite must derive from a SuiteTestEnvironment class which defines the environment for running tests for this service.  Each service has its own requirements on its test environment so each has its own SuiteTestEnvironment.

getting past security

In component tests, service security code is still run for all calls.  Component tests also don't start the identity service and the provisioner to produce valid headers.  Instead we've provided libraries which simulate the provisioner and identity to make it possible to produce valid headers.  To do this there are several steps:

  • start the service configured with a valid public key for the simulated provisioner.
  • initialize the service with a valid public key for the mocked identity service.  The service will check that the request is valid by checking the header against the simulated provisioner's public key.
  • include a valid user token header in all the calls to the service.  The service will check the user token header.  The public key provided to the service in the call to initialize is used to check the signature.

To make this possible the libraries fineract-cn-test and fineract-cn-anubis contain some supporting code:

  • TestEnvironment sets the public key for the simulated provisioner and keeps the corresponding private key around for future use.  It's a so-called test resource with a before and after clause which are run automatically by the framework.
  • TenantApplicationSecurityEnvironmentTestRule uses the TestEnvironment to create the necessary call to initialize, and to set a user token header in the REST calls to the service.  In most cases a new user context is created for each test.

testing events

Almost all changes made to service data are executed asynchronously.  That means that in order to check that they were made correctly we need some sort of notification that the change is complete.  The services notify of change completion via ActiveMQ events.  The event ID definitions are part of the API.  This means that:

  • We need to test that events are sent when asynchronous changes are complete, and
  • we need to wait on events before we can check that changes are correctly made.

To support these needs, we've created an EventRecorder which can be used to note events and to wait for events.  To use it, do four things:

  • enable it using the @EnableEventRecording annotation on your test configuration,
  • make sure that your event objects have equals and hashcode implemented,
  • create a listener for the event in question, similar to the SampleEventListener.  The listener should pass event notifications into the event listener.
  • call eventRecorder.wait(<event id>, <location identifying object>) when you're expecting an event.

Once wait matches an event, it removes that event from the list of events to match.  So if you're expecting several identical events in sequence, you can safely call wait for each of them.

Because the EventRecorder collects all events that it "sees" in memory, it is not suitable for most production uses.  It depends on the limited run times of component tests; in most other contexts its approach will be indistinguishable from a massive resource leak.  If you want to wait for a specific event in production code, consider using EventExpectation instead.

Testing for libraries

Library tests consist only of unit tests.  All other granularities of testing for the libraries are covered through their usage in the services.  Because its hard to discover bugs in a library from dependent code, this makes unit testing of libraries far more important than it is for services.  So think twice before you omit a unit test for a new library class.

Integration Tests

Integration tests are very similar to component tests except that they start multiple services instead of just one.  Integration tests are kept in a separate repository.  The only example we currently have is: fineract-cn-demo-server.  Because services need to communicate with each other, services also need the following:

  • The same public key for the provisioner across all the services.
  • The same public key for identity within a tenant across all the services.
  • The same provisioned name of at least one tenant across all the services.
  • A locally running instance of Eureka for use in service discovery.
  • A locally running instance of ActiveMQ.

We have a few classes in fineract-cn-service-starter which are intended to make this easier:


  • No labels

1 Comment

  1. Thanks Grahamfor adding links to the classes in github.  This is really useful.