Why testing Apache Fineract customizations needs its own suite
A regression suite for a customized Apache Fineract answers one question before each release: do the custom modules, COB business steps, transaction processors and business events still behave as intended, and does the stock platform still do what your products depend on. Teams need one as soon as they carry custom code in their image, because Fineract's own tests cannot know your products, charges, accounting mappings or country rules. Without a repeatable suite, every upgrade becomes a manual re-test of the loan and savings lifecycle.
Fineract is headless. It exposes REST APIs and provides no user interface of its own, so the suite should drive the API and inspect the database, not click through a screen. In practice that means scripted scenarios that create clients, open loan and savings accounts, post repayments, run end-of-day processing and then check balances, schedules and journal entries in the database. A user interface, such as the Mifos X web application, can be tested separately, but it should not be the only place where a loan rule is proven.
The README states that official Fineract releases are created quarterly, at the end of each quarter, so you will repeat this work several times a year. Confirm with your team which upstream release you are moving to, read its release notes for changes that touch your customizations, and run the full suite against a fresh database built from your own data set. The suite must be deterministic, meaning the same inputs produce the same results on every run, or it will not tell you whether a failure came from your code or from the test environment.
Which test type fits which customization
This table maps each custom artifact to the test layer that should cover it, the setup it needs and the main risk it guards against. Read each row as a starting point: the layer named is where regressions are cheapest to catch, and the setup column shows what the test needs before it can run.
| Customization | Recommended test layer | Setup needed | Main risk it guards against |
|---|---|---|---|
| Custom module services | Integration test against a running backend | Running Fineract instance on a test database, tenant configured, module packaged into the test image | Replaced or added Spring beans changing behavior that core APIs depend on after an upgrade |
| Custom Loan COB business steps | Integration test with business date control | Test loans in known states, business date set to a chosen close date, COB job triggered explicitly | Step ordering errors or side effects that only appear when the job processes many accounts |
| Custom loan transaction processors | Cucumber end-to-end test on loan repayments | Loan product that uses the processor, repayment scenarios with expected allocation amounts, fixed business date | Wrong allocation of repayments across principal, interest, fees and penalties for existing products |
| Business event serializers and Avro schemas | Integration test on the published event payload | Event publishing enabled against a test broker (ActiveMQ or Kafka), consumer that reads the schema | Downstream consumers breaking when a field is renamed, typed differently or missing |
| Custom batch jobs | Integration test that launches the job | Job registered by its database migration, job name configured, test data that the job reads and writes | Job silently skipping records, double-running, or failing to register in a fresh environment |
| Accounting effects of customizations | Cucumber end-to-end test with accounting checks | Accounting mode and mappings configured, journal entries readable through the API, known transactions | Customization posting to the wrong ledger account or leaving entries unbalanced |
| Date-dependent behavior | Integration test with business date control | Business date set per scenario, reset after each run, documented close dates | Interest, schedules and overdue logic producing different results depending on the day the test runs |
How to control business dates in tests
Loan tests depend on dates in almost every assertion. Interest accrual, repayment schedules, due dates, overdue status and arrears ageing all change with the business date, so a test that runs on the wall-clock date can pass today and fail next week. Pinning the business date for each scenario lets the suite assert exact schedule rows and exact accrued amounts, and lets a failing test be replayed on the same date by another engineer.
- Fixed disbursement dates: Set the disbursement date explicitly in every loan scenario so schedule generation starts from a known point.
- Stepwise advancement: Move the business date forward in defined steps to model each repayment due date, then assert the state after each step.
- Overdue and arrears checks: Advance past a due date without a repayment to verify overdue flags and ageing buckets for your product configuration.
- Recalculation scenarios: Fix the date before and after a prepayment or charge so recalculated schedules can be compared against expected values.
- Isolated tenant data: Keep loan scenarios on a dedicated test tenant so date changes made for one scenario do not affect another.
Confirm in the Fineract documentation how business dates are set or advanced for a test tenant, and whether that behavior is controlled by configuration or by an API call in your release.
Accounting tests assert that journal entries land on the correct date, in the correct period, with the correct balances. When the business date drifts, entries can fall into the wrong period, accrual postings can be duplicated or missed, and period-end reports can change between runs. A controlled business date lets the suite close a period, post entries and verify that the ledger totals match expected values every time.
- Period boundary cases: Test transactions posted on the last day and first day of a period, since these are where cut-off errors appear.
- Accrual postings: Advance the date across accrual runs and assert the posted amounts and the journal entry dates.
- Period closure order: Verify that entries posted after a closure are rejected or handled as your design requires.
- Reconciliation totals: Compare ledger totals against expected values derived from the scenario inputs, not against the previous run.
- Custom mapping checks: When you add ledger mappings for a custom product, assert the accounts used on each fixed date.
Confirm with your accounting team which period rules apply, and confirm in the Fineract documentation how accounting closures interact with the business date and whether back-dated entries are permitted in your configuration.
The Close of Business job processes every account on the business date and moves the tenant forward, so its results are only repeatable when the date is fixed before the job runs. COB tests usually cover custom business steps, ordering and failure recovery. Without a controlled date, the job can process different account sets across runs and the expected end-of-day state becomes hard to define.
- Defined input state: Prepare accounts with known balances and due dates, then set the business date before triggering COB.
- Custom step verification: Assert the output of your own business steps for the exact date under test.
- Repeat runs: Reset to the same starting state and date to confirm the job produces identical results on a second run.
- Failure and restart: Simulate an interruption on a chosen date and verify how the job resumes, using the same date to make the outcome comparable.
- Scale limits: Use small populations in automated suites and reserve large-volume runs for performance environments, since the project notes the job can run for hours at large scale.
Confirm in the Fineract documentation how the COB job is triggered, how it is ordered against business date changes, and what recovery behavior applies to a partially completed run.
Setting up a Fineract regression suite step by step
This sequence turns a customized Fineract into a repeatable regression suite: an isolated PostgreSQL test environment, known test data, integration and Cucumber tests, controlled business dates, COB runs and a CI gate with explicit pass criteria. Each step ends with a check that tells you whether the next one can start.
Provision an isolated Fineract test environment
Stand up a test environment that matches production in the parts your customizations touch: the Fineract version, the PostgreSQL major version, the tenant setup and the custom module build. Fineract's documentation recommends PostgreSQL, and the official Docker image ships only the PostgreSQL JDBC driver, so a MariaDB or MySQL test database would not exercise the same code path. Run the write instance and the batch instance separately if production does, because Liquibase migrations run only on write instances. Check: the environment starts from an empty database, Liquibase completes on the write instance, and the health endpoint responds for the tenant you will test against. Keep the environment disposable so every suite run starts from the same baseline.
Load a known tenant and reference data
Create a dedicated test tenant and load a fixed set of reference data: offices, staff, currencies, loan and savings products, charges, accounting rules and chart of accounts that your customizations depend on. Store this data as version-controlled scripts or API calls, not as a manually prepared database dump, so a change to a product definition shows up in review. Use the Fineract-Platform-TenantId header on every call so the data lands in the intended tenant. Check: a fresh run of the loader produces the same product and accounting IDs you reference in tests, and a second run fails cleanly or is idempotent rather than duplicating records.
Write integration tests against the running backend
Write integration tests that call the running Fineract instance over its REST API for each custom module, custom batch job, business step, transaction processor and event you have built. These tests confirm that your code works with the real Spring context, the real database and the real request path, which unit tests with mocks cannot show. Cover both success paths and the error responses your custom code returns. Authenticate with a test identity rather than the default development login, and keep the credentials out of source control. Check: the integration suite runs against the environment from step one, every custom endpoint and job has at least one passing test, and a deliberately broken custom class makes the suite fail.
Add Cucumber feature files for core scenarios
Describe the business scenarios that matter most in Cucumber feature files: loan disbursement and repayment with your products, savings deposits and withdrawals, interest posting, and the journal entries each produces. Write the steps so a product owner can read them, then bind them to API calls that check balances, schedule rows and ledger entries. Keep one scenario per business rule and make each scenario create its own client and accounts so tests do not depend on order. Check: the feature files run in the same environment, each scenario passes in isolation and in a full run, and the accounting assertions match the figures a finance reviewer has signed off for the sample data.
Control business dates in every scenario
Fineract's loan and savings behavior depends on the business date, so the suite must set it explicitly rather than rely on the system clock. Give each scenario a business date at its start, advance it in controlled steps, and reset it at the end. Record the date sequence in the test output so a failure can be traced to the day it occurred. Check: rerunning the same scenario on a different calendar day produces identical balances and schedules, and a test that forgets to set the date fails rather than passing by accident.
Run COB in the test environment
Run the Close of Business job after advancing the business date, using the same job configuration and step ordering as production, including any custom Loan COB business steps. The Fineract README notes that COB can take several hours on deployments with millions of accounts, so keep the test dataset small enough for a suite run and cover large-volume behavior in a separate performance environment. Assert the state after COB: accrued interest, overdue status, accounting postings and any custom step output. Check: COB completes with a success status, the expected accounts change state, and a second COB run for the same date does not post duplicate entries.
Wire the suite into CI with explicit pass criteria
Connect the environment provisioning, data loading, integration tests, Cucumber scenarios and COB checks into one pipeline run. Define pass criteria in writing before the first run: for example, all integration and Cucumber scenarios pass, no COB run leaves an account in an unexpected state, and the environment is torn down after each run. Store test reports and the business date log as build artifacts so auditors and change reviewers can trace evidence. Confirm the required runner, database and secret access with your own platform team. Check: a pipeline run on a clean branch passes end to end, a branch with a seeded regression fails with a report that names the failing scenario and business date, and the pass criteria are recorded alongside the pipeline definition.
Building test data for loans, accounting and COB
Design fixtures around the accounting and loan outcomes you need to prove, not around the number of records you can generate. For each scenario, define the client, the loan or savings product, the account state and the journal entries that should follow, then express those as versioned fixture files that sit next to the custom module they test. A fixture for a custom repayment allocation rule, for example, should name the product, the schedule terms, the repayment amount and the expected split across principal, interest, fees and penalties, so a failing test points to one rule. Keep product definitions in the same repository as the custom code, and bump fixture versions in the same commit as any change to a business step, processor or event schema. Ask your team to confirm how the fixtures will be loaded into the target tenant, because the setup path you choose determines how much state each scenario can rely on.
Reset state between scenarios deliberately. Business date control is the most common source of flaky results: a scenario that depends on the current date, or on a previous scenario's close of business run, will pass or fail depending on execution order. Pin the business date in each scenario, run the Close of Business job only where the scenario needs it, and assert the resulting balances and journal entries rather than relying on rows left by earlier tests. Use a fresh tenant or a database restore for suites that change accounting configuration. Keep the volume small for correctness suites, with a handful of clients per product and a few accounts per branch. The Fineract README notes that the Close of Business Day job can run for several hours on deployments with millions of accounts, so measure COB runtime and throughput in a separate performance environment with production-like volumes, and confirm the sizing and the timing targets with your own team before that work is scheduled.
Security and operational risks in test environments
Test environments for a customized Fineract often hold copies of configuration, credentials and data that look harmless but reach real systems. The cards below cover the controls that keep a shared test stack from becoming an exposure, and each one names a check you can run before the suite goes into CI.
Replace the default development login
Fineract's README documents a default development login, mifos with a matching password, that must be changed for any real deployment. Test stacks are often exposed to shared networks or CI runners before anyone remembers this. Before the environment is reachable by anyone other than the build agent, confirm that the default credentials are gone in every tenant and that the suite reads its login from a secret rather than from source. Test the change by attempting the old login from outside the network.
Keep HTTPS on test endpoints
Fineract strongly encourages HTTPS only and exposes SSL settings through environment variables such as FINERACT_SERVER_SSL_ENABLED. Test teams often turn TLS off to simplify debugging, then copy that configuration into staging. Keep TLS enabled on every shared test endpoint, including the batch and read instances, and make the suite fail fast when it is pointed at a plain HTTP address. Use test certificates issued by an internal authority that your runners trust.
Keep real customer records out
Loan, savings and KYC test scenarios are easiest to write from production extracts, but a copy of a live tenant carries personal data into a less controlled environment. Generate synthetic clients, accounts and identity document values for every scenario, and record which fixtures are safe to commit. Ask whether any migrated or sampled data was ever loaded into the test database, and purge it if so. Label fixture files so a reviewer can see that they are synthetic.
Protect test credentials in CI
A regression suite needs administrative tokens, tenant identifiers and database access to reset state, which makes the CI system a high-value target. Store these values in the pipeline's secret store, scope each job to the tenant it needs, and mask values in logs. Rotate the credentials on a schedule and after any change to runner membership. Confirm that pull requests from forks cannot read the secrets used by the integration job.
Isolate test tenants from production
Fineract is multi-tenant, with each API request carrying a Fineract-Platform-TenantId header and tenant connections defined in a tenants database. A misconfigured test client that sends the production tenant identifier can post real transactions. Give the test stack its own database server, its own tenants database and its own network path, so that no production hostname is reachable from the suite. Add an assertion that refuses to run if the configured tenant does not match the test naming pattern.
Clean up tenant data after runs
Scenarios that create clients, loans and journal entries leave state behind, and later runs can fail on leftovers or read stale balances. Decide whether each suite run creates a fresh tenant, truncates its own test records, or resets a snapshot. Whichever you choose, run the cleanup even when a test fails, and record the tenant identifier so that orphaned tenants can be found and removed. Check that no test tenant survives beyond the pipeline run that created it.
Questions teams ask before testing customized Fineract
What drives the effort of a regression suite for customized Fineract?
The effort follows the number of customizations and how far they reach into loan, savings, accounting and end-of-day behavior. Each custom business step, transaction processor, custom job or event changes outcomes that the suite must check, so each one adds scenarios, fixtures and expected results. Effort also rises with the number of products and configuration variants you run in production, since each combination can behave differently. Ask for the test matrix to be listed by product and customization before agreeing a price.
Can our internal team build this suite without outside help?
Yes, if the team has Java or JVM test experience, knows Fineract's REST API and has someone who owns the business rules being tested. The hard parts are usually not the tooling but deciding what the correct loan, accrual and accounting result should be, and keeping test data realistic. Many teams start in-house and bring in a partner for the initial framework, the first set of end-to-end scenarios or a review of coverage gaps. Confirm who will maintain the suite after handover before deciding how much to outsource.
How long does setup take as the number of customizations grows?
Setup time grows with the number of customizations and the number of business rules each one changes, so a single custom repayment processor needs far less work than a set of custom end-of-day steps across several products. There is no fixed duration in the project's documentation, so the timeline should come from a written scope that lists each customization and the scenarios it needs. Ask for setup and ongoing maintenance to be quoted separately, because the second recurs with every release and every change to products.
What do we need to provide for the testing work?
You need at least one environment that matches production configuration closely enough to test, including the database platform, which the project recommends as PostgreSQL. You also need named owners for test data, usually a product owner who can confirm expected loan schedules, interest and accounting mappings, and business acceptance criteria written as clear pass or fail statements. Without these owners, expected results are guessed, and the suite will encode mistakes. Agree on who signs off each scenario before work starts.
How do we keep the suite current across Fineract releases?
The Fineract README states that official releases are created quarterly, at the end of each quarter, so a suite needs a planned review at each upgrade rather than a one-off effort. Run the existing scenarios against the new release in a non-production environment, then update only the tests whose expected behavior changed and add tests for new features you adopt. Keep customizations in a separate module with its own tests so upgrade failures are easy to locate. Ask any partner how upgrade testing is scheduled and billed.
What is included in a fixed-scope testing engagement?
A fixed-scope engagement is agreed in writing before work starts and normally lists the customizations covered, the test layers to build, the environments used, the deliverables and the timeline. Typical items are an end-to-end Cucumber suite for key business flows, integration tests against a running backend, controls for business date handling, and test data sets for loans, accounting and end-of-day processing. It should also state what is excluded, such as performance testing or the maintenance of the suite after handover. DeployOpen does not publish a price list, so ask for a quote that follows from that written scope.
How do we test business date and end-of-day behavior reliably?
Tests that depend on the current system date become flaky, so the suite should control the business date explicitly for every scenario and advance it in known steps. Run the end-of-business-day job in the test environment against the same date sequence each time, and check balances, schedules and accounting entries after each run. The Fineract README notes that this job can run for several hours on deployments with millions of accounts, so keep the end-of-day fixtures small in regression runs and reserve large-volume runs for a separate schedule.
How DeployOpen scopes and delivers Fineract test suites
DeployOpen plans, builds, documents and hands over regression suites for customized Fineract deployments inside the client's own infrastructure. Engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before testing work starts.
How we scope
- Inventory which customizations, custom modules and business steps the suite must cover before any test is written.
- Agree which business dates, COB runs, loan products and accounting mappings need regression coverage in writing.
- Confirm the Fineract version, PostgreSQL target and test environments the suite will run against.
How we build
- Write Cucumber end-to-end scenarios for loan, savings and accounting flows that matter to your product rules.
- Build integration tests that run against a live Fineract backend, with controlled business date and test data setup.
- Structure test data fixtures so COB and ledger assertions stay repeatable across runs and environments.
How we hand over
- Deliver the test code, fixtures and run instructions in a repository your team owns and can operate.
- Document each suite's coverage, known gaps and the steps needed to add tests for new customizations.
- Walk your engineers through running the suite locally and in your pipeline, and record open decisions.
How we support
- Offer ongoing support and continuous testing as a retainer, sized to the written scope of your suite.
- Review regression results when you upgrade Fineract releases or change custom modules, and update affected tests.
- Confirm retainer terms, response expectations and included activities in writing before support begins.

