[
https://issues.apache.org/jira/browse/FINERACT-2609?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Krishna Mewara updated FINERACT-2609:
-------------------------------------
Description:
h2. What this is
Moving Fineract's integration tests off raw RestAssured HTTP and onto the
generated Feign
client ({{{}fineract-client-feign{}}}). I'm doing this Strangler Fig style: the
new Feign helpers
sit alongside the existing RestAssured ones and tests move over in batches, so
nothing breaks
in one go.
The reason is simple. RestAssured tests build JSON by hand, so a broken API
contract only
shows up when CI runs. Feign is generated from the OpenAPI spec, so the same
mistake won't
compile.
This is the umbrella issue. To close: FINERACT-2454
h2. When I'll call this done
All five of these:
# No {{io.restassured}} or {{Utils.performServer*}} left in any test outside
{{common/}}
# {{BaseLoanIntegrationTest}} and {{BaseSavingsIntegrationTest}} are deleted
# The RestAssured-only helpers in {{common/}} are gone or reduced to thin
Feign wrappers
# The build fails if someone adds a new {{io.restassured}} import
# {{Utils.java}} keeps only the non-HTTP bits
({{{}uniqueRandomStringGenerator{}}}, the login helper)
Number of helpers written isn't the finish line. Number of tests actually moved
is.
h2. Scope note
The 50 Retrofit-based tests are out of scope here. They're already type-safe,
so they're well
behind the 148 RestAssured ones in priority. {{IntegrationTest}} stays until
someone picks them
up in a follow-up.
h2. Where I started — 2026-05-28
Frozen so we can see the distance travelled. I won't edit this table again.
||Metric||Count||
|Helper files in {{common/}}|123|
|Helpers using RestAssured only|53|
|Helpers using both RestAssured and Retrofit|18|
|Helpers already on Retrofit ({{{}Calls.ok{}}})|35|
|Helpers with no RestAssured (pure utility)|51|
|Test files (excluding {{common/}} and Feign)|312|
|Test files importing RestAssured|197 of 312 (63%)|
|Feign test files|10|
|Feign helpers|17|
|Feign modules|7|
|Wrapper interfaces|4|
h2. Where it stands now — 2026-07-31
||Metric||Start||Now||Left||
|Tests on Feign|10|*135* (869 test methods)|—|
|Tests on RestAssured|197|*148* (1,050 methods, ~82k lines)|148|
|Tests on Retrofit (neither RA nor Feign)|—|*50*|out of scope|
|Total test classes|312|333|—|
|Feign helpers|17|*30*|—|
|Feign modules|7|*12*|—|
|{{common/}} helpers still on RestAssured|53|*52*|52|
|{{Utils.java}} methods marked {{@Deprecated}}|0|*24 of 64*|—|
49 test files have come off RestAssured so far. The helper layer is more or
less built at this
point; the bulk of what's left is moving the test files themselves.
If anyone wants to check these numbers, this is how I count them:
{code:bash}
cd integration-tests/src/test/java/org/apache/fineract/integrationtests
RA='io\.restassured|RequestSpecification|Utils\.performServer'
FG='client\.feign|Feign[A-Z][A-Za-z]*Helper|FeignLoanTestBase|FeignIntegrationTest'
grep -rl "@Test" . --include=*.java | wc -l
# total
grep -rl "@Test" . --include=*.java | xargs grep -lE "$RA" | wc -l
# on RestAssured
grep -rl "@Test" . --include=*.java | xargs grep -LE "$RA" | xargs grep -lE
"$FG" | wc -l # on Feign
grep -rl "@Test" . --include=*.java | xargs grep -LE "$RA" | xargs grep -LE
"$FG" | wc -l # on Retrofit
grep -rlE "$RA" ./common --include=*.java | wc -l
# RA helpers
{code}
h2. What's left, by area
||Area||Classes||Tests||Blocked on||
|Loan|57|470|nothing|
|Savings and deposits|19|199|deposit helpers not written yet|
|Organisation and misc|37|181|nothing|
|Jobs and batch|5|69|nothing|
|Client|9|55|nothing|
|Accounting|11|40|nothing|
|Datatable and entity|4|22|nothing|
|Reporting, shares, bulkimport, group|10|36|nothing|
|*Total*|*148*|*1,050*| |
These six are big enough that each one needs its own PR:
||Class||Tests||Lines||
|{{AdvancedPaymentAllocationLoanRepaymentScheduleTest}}|86|6,625|
|{{ClientLoanIntegrationTest}}|66|8,354|
|{{FixedDepositTest}}|47|3,200|
|{{ClientSavingsIntegrationTest}}|45|3,662|
|{{BatchApiTest}}|40|2,750|
|{{RecurringDepositTest}}|38|3,128|
h2. Old classes on the way out
||Class||Start||Now||Blocked on||
|{{BaseLoanIntegrationTest}}|121|*53* (52 subclasses)|nothing|
|{{BaseSavingsIntegrationTest}}|—|*3 subclasses*|savings batch|
|{{ClientHelper}}|216|*170*|client batch|
|{{LoanTransactionHelper}}|118|*125*|loan batch|
|{{SavingsAccountHelper}}|37|*35*|savings batch|
|{{Utils.java}}|309|*345*|everything else|
|RestAssured-only helpers in {{common/}}|53|*52*|per-area Feign helpers|
Worth flagging: {{Utils.java}} and {{LoanTransactionHelper}} both went
{*}up{*}, not down. New tests are still being written against the old layer
while I'm migrating the old ones off it.
That's what exit criterion #4 is for.
h2. Things I know are still open
A few gaps I'd record:
* {{FeignLoanHelper}} still has a handful of methods that take a JSON string
and post it
through the old RestAssured utility. Any test going down those paths isn't
genuinely off
RestAssured, so the 135 figure above is slightly generous. Separate ticket for
it.
* One test living under the Feign folder still builds a RestAssured request
spec itself.
* {{FeignLoanTestBase}} is now 1,562 lines, 224 methods and 99 subclasses.
It's drifting
towards being the same kind of god class I'm trying to delete. I'd rather split
it once the
area batches have landed than churn it mid-migration.
h2. How the Feign side is put together
Helpers live under {{client/feign/helpers/}} (30 of them so far). Each takes
{{FineractFeignClient}} in the constructor, calls the API through {{ok(...)}} /
{{{}fail(...) }}from \{{{}FeignCalls{}}}, and returns the whole response object
rather than just an ID.
Request building lives in {{client/feign/modules/}} (12 files: the
{{{}*RequestBuilders{}}}, {{{}LoanProductTemplates{}}},
{{{}LoanTestValidators{}}}, test data holders). Tests extend
{{{}FeignIntegrationTest{}}}, {{FeignLoanTestBase}} or
{{{}FeignSavingsTestBase{}}}.
Both clients are on the classpath at the same time. The {{exclude}} in
\{{dependencies.gradle }}keeps the duplicate model classes apart.
Conventions I've settled on as I've gone:
* Feign helpers are new classes, not extra methods bolted onto the old helpers
* Wrapper interfaces only where the generated name is unreadable
({{{}create6(){}}} → {{{}createClient(){}}})
* New shared code goes in a helper or a module, never on the test base.
One-off methods stay {{private}} in the test that needs them.
* Where the generated model genuinely can't express a call, I drop to a small
raw-HTTP helper and leave a comment explaining why. Never RestAssured. If the
gap is only a field or two, I fix the Swagger DTO and regenerate instead. I
keep a running list of every one of these so none of them get forgotten.
* Coexistence was the right call for the first phase. It ends when criterion
#4 lands.
h2. PR roadmap
I'm keeping the PR count deliberately low and putting the detail into commits
instead. Each
remaining PR is one area, broken into commits that each build and pass on their
own, so they
can be reviewed one at a time without the PR itself becoming unreviewable.
||PR||What||Files||Tests||Ticket||GitHub||Status||
|1|Savings helper, test base, Swagger fields|—|—|FINERACT-2620, 2626|#5907,
#5934|Merged|
|2|Client helper build-out, Swagger fields|—|—|FINERACT-2631, 2632|#5954,
#5955|Merged|
|3|Charges and delinquency helpers|—|—|FINERACT-2635|#5978|Merged|
|4|Loan helper: charges, transitions,
disbursement|—|—|FINERACT-2640|#5997|Merged|
|5|Loan tests, 4 tiers|—|—|FINERACT-2649|4 PRs, see ticket|Last tier in review|
|6|Group and centre|3|16|FINERACT-2718|#6194|*In review — I'm here*|
|7|Loan: money movement|21|360|to raise|—|Next|
|8|Loan: lifecycle, product, COB|33|113|to raise|—|Not started|
|9|Savings and deposits|20|209|to raise|—|Not started|
|10|Client, charges, accounting|20|101|to raise|—|Not started|
|11|Organisation, jobs, batch, config|33|228|to raise|—|Not started|
|12|Closeout|—|—|to raise|—|Not started|
|S1|Bulk import, shares, reporting, smoke tests|21|39|stretch|—|Not started|
|S2|Split {{FeignLoanTestBase}}|—|—|stretch|—|Not started|
|S3|The 50 Retrofit tests|50|—|stretch|—|Not started|
PRs 7 to 11 add up to 127 files and 1,011 tests. S1 covers the last 21 files
and 39 tests.
h2. Log
One line per merge. I don't edit old entries, only add to the bottom.
||Date||PR||Ticket||What went in||
|2026-06-04|[#5934|https://github.com/apache/fineract/pull/5934]|FINERACT-2626|Added
{{rejectedOnDate}} and {{withdrawnOnDate}} to the savings Swagger spec. The
generated model was missing them, so a reject call silently did nothing. Needed
before #5907 could land.|
|2026-06-07|[#5907|https://github.com/apache/fineract/pull/5907]|FINERACT-2620|First
area done end to end. 10 new files: savings helper, test base, lifecycle
extension, request builders, and an 8-test lifecycle test to prove the pattern
works.|
|2026-06-09|[#5955|https://github.com/apache/fineract/pull/5955]|FINERACT-2632|Client
Swagger fields the server accepts but the model didn't have:
{{{}submittedOnDate{}}}, the withdrawal fields, and
{{{}firstname{}}}/{{{}lastname{}}} on PUT.|
|2026-06-09|[#5954|https://github.com/apache/fineract/pull/5954]|FINERACT-2631|{{FeignClientHelper}}
from 4 methods to 16, plus {{ClientRequestBuilders}} and a full client
lifecycle test.|
|2026-06-11|[#5978|https://github.com/apache/fineract/pull/5978]|FINERACT-2635|Charges
and delinquency: {{FeignChargesHelper}} (14 methods),
{{{}ChargeRequestBuilders{}}}, and a 7-test delinquency lifecycle. Also fixed 4
fields missing from {{PostChargesRequest}} that were sending nulls on PUT.|
|2026-06-21|[#5997|https://github.com/apache/fineract/pull/5997]|FINERACT-2640|Loan
helper grew charge management, state transitions, disbursement details and
reschedule: 11 charge methods, 6 transitions, 4 disbursement methods.|
|from 2026-06-26|4 PRs|FINERACT-2649|The loan test migration, done in four
tiers. Tier 4 is the big one at 92 files. Per-PR detail and review notes are on
that ticket rather than duplicated here. Final tier is in review.|
|2026-07-28|[#6194|https://github.com/apache/fineract/pull/6194]|FINERACT-2718|Group
and centre. New helpers for group, centre, staff and GLIM. Migrated
{{{}CenterIntegrationTest{}}}, {{GroupTest}} and {{GroupLoanIntegrationTest}}
(16 tests) and retired a raw-HTTP stopgap helper that was Feign in name only.
No changes to the shared loan test base. In review.|
was:
h2. Overview
Migrate Fineract's integration test infrastructure from raw RestAssured HTTP
calls to the
type-safe Feign client ({{{}fineract-client-feign{}}}). Following the Strangler
Fig pattern —
new Feign helpers coexist alongside legacy RestAssured helpers, tests migrated
incrementally.
No big bang rewrite.
To close: FINERACT-2454
h2. Why migrate?
* RestAssured tests use raw JSON strings and untyped HTTP — brittle, no
compile-time safety
* Feign client is type-safe, auto-generated from the OpenAPI spec
* Catch API contract drift at compile time, not at runtime in CI
h2. Current State
||Metric||Count||
|Total helper files in {{common/}}|123|
|Helpers with RestAssured only (unmigrated)|53|
|Helpers with both RestAssured + Retrofit (mixed)|18|
|Helpers already using Retrofit ({{{}Calls.ok{}}})|35|
|Helpers with NO RestAssured (pure utility)|51|
|Total test files (excluding {{common/}} and Feign)|312|
|Test files directly importing RestAssured|197 of 312 (63%)|
|Existing Feign test files|10|
|Existing Feign helpers|17|
|Existing Feign modules|7|
|Existing wrapper interfaces|4|
h2. Existing Feign Infrastructure
These are already merged and in {{{}upstream/develop{}}}. Listed here so the
scope of remaining
work is clear and we don't accidentally duplicate what already exists.
h3. Base Classes
||Class||Role||Replaces||
|{{FeignIntegrationTest}}|Root base for all Feign tests|{{IntegrationTest}}
(Retrofit-based)|
|{{FeignLoanTestBase}}|Wires all loan helpers, ready for loan test
migration|{{BaseLoanIntegrationTest}} (decommission target)|
h3. Helpers (under {{{}client/feign/helpers/{}}})
||Helper||Replaces||Coverage||
|{{FeignLoanHelper}}|{{LoanTransactionHelper}} (3,250 lines)|~16% — core
lifecycle only|
|{{FeignTransactionHelper}}|Extracted from {{LoanTransactionHelper}}|repayment,
chargeOff, reAge, inline COB|
|{{FeignClientHelper}}|{{ClientHelper}} (1,207 lines)|~6 of ~50 methods|
|{{FeignAccountHelper}}|{{AccountHelper}}|GL account lookup|
|{{FeignBusinessDateHelper}}|{{BusinessDateHelper}}|Full functional replacement|
|{{FeignJournalEntryHelper}}|{{JournalEntryHelper}}|Journal verification|
|{{FeignOfficeHelper}}|{{OfficeHelper}}|Full functional replacement|
|{{FeignSchedulerHelper}}|{{SchedulerJobHelper}}|Full functional replacement|
|{{FeignGlobalConfigurationHelper}}|{{GlobalConfigurationHelper}}|Full
functional replacement|
|{{FeignWorkingCapitalLoanHelper}}|{{WorkingCapitalLoanHelper}}|WC loan ops|
|{{{}FeignSearchHelper{}}}, {{{}FeignTaxComponentHelper{}}},
{{FeignTaxGroupHelper}}|No legacy equivalent|New capability|
|{{{}FeignNotificationHelper{}}}, {{{}FeignLoanOriginatorHelper{}}},
{{FeignExternalEventHelper}}|No legacy equivalent|New capability|
h2. Migration Tiers
h3. Tier 1 — High-Impact
||Helper||Importers||RestAssured %||Feign existence||
|{{Utils.java}}|309|100%|None — migrate last|
|{{ClientHelper.java}}|216|~80%|{{FeignClientHelper}} (6 methods — extend in PR
2)|
|{{BaseLoanIntegrationTest.java}}|121|~95%|{{FeignLoanTestBase}} — exists,
decommission after charge coverage added|
|{{LoanTransactionHelper.java}}|118|~55%|{{FeignLoanHelper}} +
{{FeignTransactionHelper}} — extend in PR 4|
|{{SavingsAccountHelper.java}}|37|~97%|None — greenfield target for PR 1|
h3. Tier 2 — Medium-Impact
||Helper||Importers||Feign Beachhead||
|{{BusinessDateHelper}}|61|{{FeignBusinessDateHelper}} (done)|
|{{AccountHelper}}|60|{{FeignAccountHelper}} (done)|
|{{ChargesHelper}}|55|None — PR 3|
|{{DelinquencyBucketsHelper}}|36|None — PR 3|
|{{SchedulerJobHelper}}|35|{{FeignSchedulerHelper}} (done)|
|{{GlobalConfigurationHelper}}|26|{{FeignGlobalConfigurationHelper}} (done)|
|{{JournalEntryHelper}}|24|{{FeignJournalEntryHelper}} (done)|
|{{OfficeHelper}}|19|{{FeignOfficeHelper}} (done)|
|{{GroupHelper}}|17|None — PR 5|
h3. Tier 3 — Low-Impact / Niche
{{BatchHelper}} (7 importers), {{{}FixedDepositAccountHelper{}}},
{{{}RecurringDepositAccountHelper{}}},
and domain-specific helpers with <10 importers each.
h3. Foundation Layer
{{Utils.java}} (309 importers) is the RestAssured gateway that nearly all
helpers delegate to.
{*}Deprecate last{*}, only after all helpers above are migrated. The
RA-specific methods
({{{}initializeRESTAssured(){}}}, HTTP request wrappers) can be {{@Deprecated}}
progressively —
but {{uniqueRandomStringGenerator()}} and
{{loginIntoServerAndGetBase64EncodedAuthenticationKey()}}
are used widely even in non-RA code, so those stay until their callers are
migrated.
h2. Architecture Pattern
New {{FeignXxxHelper}} classes live under {{{}client/feign/helpers/{}}}. They:
* Take {{FineractFeignClient}} via constructor injection
* Use {{ok(() -> ...)}} for API calls (via {{FeignCalls}} utility)
* Tests extend {{FeignIntegrationTest}} base class
* Share the same model classes ({{{}org.apache.fineract.client.models.*{}}})
as Retrofit
Legacy and Feign helpers coexist — both {{fineract-client}} (Retrofit) and
{{fineract-client-feign}} are on the classpath simultaneously. The {{exclude}}
in
{{dependencies.gradle}} prevents duplicate model classes.
h2. Key Design Decisions
# *Separate Feign helper classes* — not dual methods in existing helpers
# Both clients share model classes — no conflicts
# Thin wrapper interfaces only for APIs with unreadable generated names (e.g.
{{create6()}} → {{{}createClient(){}}})
# Coexistence over replacement — 197 test files still use RestAssured, helper
migration enables but doesn't force test-level migration
h2. Decommission Plan
These classes will be retired once their Feign replacement has full method
coverage.
Both old and new coexist until importer counts reach zero.
||Class||Importers||Depends On||
|{{BaseLoanIntegrationTest}}|121|PR 4 (charge management added to
{{{}FeignLoanHelper{}}})|
|{{BaseSavingsIntegrationTest}}|—|PR 1 ({{{}FeignSavingsHelper{}}} +
{{{}FeignSavingsTestBase{}}})|
h2. PR Roadmap
h3. Core PRs
||PR||Scope||Status||
|PR 1|{{FeignSavingsHelper}} + {{FeignSavingsTestBase}} + first savings
test|Done|
|PR 2|Extend {{FeignClientHelper}} (update, delete, search, status
transitions)|Done|
|PR 3|{{FeignChargesHelper}} + {{FeignDelinquencyHelper}}|Done|
|PR 4|Extend {{FeignLoanHelper}} — charge management + remaining disbursal
methods|Done|
|PR 5|{{FeignGroupHelper}} + {{FeignCenterHelper}}|Not Started|
|PR 6|Migrate 5–10 simple test files end-to-end from RestAssured to Feign|Not
Started|
h3. Stretch PRs
||PR||Scope||Status||
|PR S1|Batch migrate {{BaseLoanIntegrationTest}} importers to
{{FeignLoanTestBase}} (after PR 4)|In Review|
|PR S2|{{FeignFixedDepositHelper}} + {{FeignRecurringDepositHelper}}|Not
Started|
|PR S3|{{@Deprecated}} on RestAssured-specific methods in {{Utils.java}}|Not
Started|
h2. Progress Log
||Date||PR||Ticket||Notes||
|2026-06-04|[#5934|https://github.com/apache/fineract/pull/5934]|FINERACT-2626|Added
missing `rejectedOnDate` and `withdrawnOnDate` fields to
`PostSavingsAccountsAccountIdRequest` in Savings Swagger spec. Prerequisite for
#5907 the generated Feign client model was missing these fields, causing the
reject command to silently fail.|
|2026-06-07|[#5907|https://github.com/apache/fineract/pull/5907]|FINERACT-2620|Type-safe
Feign-based helpers and integration tests for Savings domain. 10 new files:
`FeignSavingsHelper`, `FeignSavingsProductHelper`,
`FeignSavingsTransactionHelper`, `FeignSavingsTestBase`,
`FeignSavingsLifecycleExtension`, `SavingsRequestBuilders`, `SavingsTestData`,
`FeignTestConstants`, and `FeignSavingsLifecycleTest` (8 tests). Helpers return
full response objects for reusability. Depends on #5934 for correct
`rejectedOnDate` support.|
|2026-06-09|[#5955|https://github.com/apache/fineract/pull/5955]|FINERACT-2632|Added
missing fields to `PostClientsRequest` (submittedOnDate),
`PostClientsClientIdRequest` (withdrawalDate, withdrawalReasonId,
reopenedDate), and `PutClientsClientIdRequest` (firstname, lastname) so the
generated Feign client models match what the server actually accepts.
Prerequisite for #5954.|
|2026-06-09|[#5954|https://github.com/apache/fineract/pull/5954]|FINERACT-2631|Extends
FeignClientHelper from 4 to 16 methods. Adds `ClientRequestBuilders` and
`FeignClientLifecycleTest` covering the full client lifecycle: pending
creation, activate, close, reject, reactivate, withdraw, undo-reject,
undo-withdraw, update, delete, search, and account retrieval. Depends on #5955.|
|2026-06-11|[#5978|https://github.com/apache/fineract/pull/5978]|FINERACT-2635|Extends
the Feign migration to the Charges and Delinquency domains. New files:
`ChargeRequestBuilders` (static typed factories mirroring
`ClientRequestBuilders`), `FeignChargesHelper` (14 methods across `ChargesApi`
and `ClientChargesApi`), `FeignDelinquencyLifecycleTest` (7 ordered E2E tests
via native `DelinquencyBucketsHelper`/`DelinquencyRangesHelper`). Fixed Swagger
spec gap in `ChargesApiResourceSwagger` — 4 fields missing from
`PostChargesRequest` caused silent null sends on PUT. Refactored 3
enricher/report tests to use `chargesHelper.createLoanDisbursementCharge()`.
`LoanChargesApi` and loan delinquency actions deferred to PR 4.|
|2026-06-21|[#5997|https://github.com/apache/fineract/pull/5997]|FINERACT-2640|Extends
`FeignLoanHelper` with loan charge management, state transitions, disbursement
details, and reschedule methods (PR 4 of the roadmap). 3 files, +284 −9. Added
11 loan charge lifecycle methods (`addLoanCharge`, `getLoanCharges`,
`updateLoanCharge`, `deleteLoanCharge`, `waiveLoanCharge`, `payLoanCharge`,
`adjustLoanCharge`, etc.), 6 state-transition methods (`closeLoan`,
`closeAsRescheduled`, `forecloseLoan`, `assignLoanOfficer`,
`unassignLoanOfficer`, `recoverGuarantee`), disbursement detail methods
(`addAndDeleteDisbursementDetail`, `getDisbursementDetail`,
`updateDisbursementDate`, `modifyAvailableDisbursementAmount`), and reschedule
methods via `RescheduleLoansApi`. Extended `LoanRequestBuilders` with static
typed factories for all new operations. Updated `FeignLoanTestBase` with
delegate methods.|
|2026-06-26|[#6005|https://github.com/apache/fineract/pull/6005]|FINERACT-2649|Migrates
5 Tier 1 loan integration tests from `BaseLoanIntegrationTest` (REST-assured)
to `FeignLoanTestBase` (Feign client). 15 files, +927 −229. Migrated tests:
`LoanPrepayAmountTest`, `LoanProductWithChargeOffBehaviourTest`,
`LoanInterestRateFrequencyTest`, `LoanDueCalculationTest`,
`FixedLengthLoanProductIntegrationTest`. Infrastructure additions:
`FeignRawHttpHelper` for raw JSON when explicit nulls are needed,
`LoanProductTemplates` (3 templates), `LoanTestValidators`
(`verifyRepaymentSchedule`, `verifyTransactions`). Extended `FeignLoanTestBase`
with builders, installment/transaction wrappers, and utilities from
`BaseLoanIntegrationTest`. Bug fixes: `FeignClientHelper.createClient()` uses
fixed past activation date matching `ClientHelper.DEFAULT_DATE`;
`FeignBusinessDateHelper` date-format overloads; automatic ISO vs
human-readable date detection in `runAt()`/`updateBusinessDate()`.|
> Migrate integration test helpers from RestAssured to Feign client
> -----------------------------------------------------------------
>
> Key: FINERACT-2609
> URL: https://issues.apache.org/jira/browse/FINERACT-2609
> Project: Apache Fineract
> Issue Type: Improvement
> Reporter: Krishna Mewara
> Assignee: Krishna Mewara
> Priority: Trivial
> Labels: beginner, beginner-friendly, feign-migration,
> test-infrastructure
>
> h2. What this is
> Moving Fineract's integration tests off raw RestAssured HTTP and onto the
> generated Feign
> client ({{{}fineract-client-feign{}}}). I'm doing this Strangler Fig style:
> the new Feign helpers
> sit alongside the existing RestAssured ones and tests move over in batches,
> so nothing breaks
> in one go.
> The reason is simple. RestAssured tests build JSON by hand, so a broken API
> contract only
> shows up when CI runs. Feign is generated from the OpenAPI spec, so the same
> mistake won't
> compile.
> This is the umbrella issue. To close: FINERACT-2454
> h2. When I'll call this done
> All five of these:
> # No {{io.restassured}} or {{Utils.performServer*}} left in any test outside
> {{common/}}
> # {{BaseLoanIntegrationTest}} and {{BaseSavingsIntegrationTest}} are deleted
> # The RestAssured-only helpers in {{common/}} are gone or reduced to thin
> Feign wrappers
> # The build fails if someone adds a new {{io.restassured}} import
> # {{Utils.java}} keeps only the non-HTTP bits
> ({{{}uniqueRandomStringGenerator{}}}, the login helper)
> Number of helpers written isn't the finish line. Number of tests actually
> moved is.
> h2. Scope note
> The 50 Retrofit-based tests are out of scope here. They're already type-safe,
> so they're well
> behind the 148 RestAssured ones in priority. {{IntegrationTest}} stays until
> someone picks them
> up in a follow-up.
> h2. Where I started — 2026-05-28
> Frozen so we can see the distance travelled. I won't edit this table again.
> ||Metric||Count||
> |Helper files in {{common/}}|123|
> |Helpers using RestAssured only|53|
> |Helpers using both RestAssured and Retrofit|18|
> |Helpers already on Retrofit ({{{}Calls.ok{}}})|35|
> |Helpers with no RestAssured (pure utility)|51|
> |Test files (excluding {{common/}} and Feign)|312|
> |Test files importing RestAssured|197 of 312 (63%)|
> |Feign test files|10|
> |Feign helpers|17|
> |Feign modules|7|
> |Wrapper interfaces|4|
> h2. Where it stands now — 2026-07-31
> ||Metric||Start||Now||Left||
> |Tests on Feign|10|*135* (869 test methods)|—|
> |Tests on RestAssured|197|*148* (1,050 methods, ~82k lines)|148|
> |Tests on Retrofit (neither RA nor Feign)|—|*50*|out of scope|
> |Total test classes|312|333|—|
> |Feign helpers|17|*30*|—|
> |Feign modules|7|*12*|—|
> |{{common/}} helpers still on RestAssured|53|*52*|52|
> |{{Utils.java}} methods marked {{@Deprecated}}|0|*24 of 64*|—|
> 49 test files have come off RestAssured so far. The helper layer is more or
> less built at this
> point; the bulk of what's left is moving the test files themselves.
> If anyone wants to check these numbers, this is how I count them:
> {code:bash}
> cd integration-tests/src/test/java/org/apache/fineract/integrationtests
> RA='io\.restassured|RequestSpecification|Utils\.performServer'
> FG='client\.feign|Feign[A-Z][A-Za-z]*Helper|FeignLoanTestBase|FeignIntegrationTest'
> grep -rl "@Test" . --include=*.java | wc -l
> # total
> grep -rl "@Test" . --include=*.java | xargs grep -lE "$RA" | wc -l
> # on RestAssured
> grep -rl "@Test" . --include=*.java | xargs grep -LE "$RA" | xargs grep -lE
> "$FG" | wc -l # on Feign
> grep -rl "@Test" . --include=*.java | xargs grep -LE "$RA" | xargs grep -LE
> "$FG" | wc -l # on Retrofit
> grep -rlE "$RA" ./common --include=*.java | wc -l
> # RA helpers
> {code}
> h2. What's left, by area
> ||Area||Classes||Tests||Blocked on||
> |Loan|57|470|nothing|
> |Savings and deposits|19|199|deposit helpers not written yet|
> |Organisation and misc|37|181|nothing|
> |Jobs and batch|5|69|nothing|
> |Client|9|55|nothing|
> |Accounting|11|40|nothing|
> |Datatable and entity|4|22|nothing|
> |Reporting, shares, bulkimport, group|10|36|nothing|
> |*Total*|*148*|*1,050*| |
> These six are big enough that each one needs its own PR:
> ||Class||Tests||Lines||
> |{{AdvancedPaymentAllocationLoanRepaymentScheduleTest}}|86|6,625|
> |{{ClientLoanIntegrationTest}}|66|8,354|
> |{{FixedDepositTest}}|47|3,200|
> |{{ClientSavingsIntegrationTest}}|45|3,662|
> |{{BatchApiTest}}|40|2,750|
> |{{RecurringDepositTest}}|38|3,128|
> h2. Old classes on the way out
> ||Class||Start||Now||Blocked on||
> |{{BaseLoanIntegrationTest}}|121|*53* (52 subclasses)|nothing|
> |{{BaseSavingsIntegrationTest}}|—|*3 subclasses*|savings batch|
> |{{ClientHelper}}|216|*170*|client batch|
> |{{LoanTransactionHelper}}|118|*125*|loan batch|
> |{{SavingsAccountHelper}}|37|*35*|savings batch|
> |{{Utils.java}}|309|*345*|everything else|
> |RestAssured-only helpers in {{common/}}|53|*52*|per-area Feign helpers|
> Worth flagging: {{Utils.java}} and {{LoanTransactionHelper}} both went
> {*}up{*}, not down. New tests are still being written against the old layer
> while I'm migrating the old ones off it.
> That's what exit criterion #4 is for.
> h2. Things I know are still open
> A few gaps I'd record:
> * {{FeignLoanHelper}} still has a handful of methods that take a JSON string
> and post it
> through the old RestAssured utility. Any test going down those paths isn't
> genuinely off
> RestAssured, so the 135 figure above is slightly generous. Separate ticket
> for it.
> * One test living under the Feign folder still builds a RestAssured request
> spec itself.
> * {{FeignLoanTestBase}} is now 1,562 lines, 224 methods and 99 subclasses.
> It's drifting
> towards being the same kind of god class I'm trying to delete. I'd rather
> split it once the
> area batches have landed than churn it mid-migration.
> h2. How the Feign side is put together
> Helpers live under {{client/feign/helpers/}} (30 of them so far). Each takes
> {{FineractFeignClient}} in the constructor, calls the API through {{ok(...)}}
> / {{{}fail(...) }}from \{{{}FeignCalls{}}}, and returns the whole response
> object rather than just an ID.
> Request building lives in {{client/feign/modules/}} (12 files: the
> {{{}*RequestBuilders{}}}, {{{}LoanProductTemplates{}}},
> {{{}LoanTestValidators{}}}, test data holders). Tests extend
> {{{}FeignIntegrationTest{}}}, {{FeignLoanTestBase}} or
> {{{}FeignSavingsTestBase{}}}.
> Both clients are on the classpath at the same time. The {{exclude}} in
> \{{dependencies.gradle }}keeps the duplicate model classes apart.
> Conventions I've settled on as I've gone:
> * Feign helpers are new classes, not extra methods bolted onto the old
> helpers
> * Wrapper interfaces only where the generated name is unreadable
> ({{{}create6(){}}} → {{{}createClient(){}}})
> * New shared code goes in a helper or a module, never on the test base.
> One-off methods stay {{private}} in the test that needs them.
> * Where the generated model genuinely can't express a call, I drop to a
> small raw-HTTP helper and leave a comment explaining why. Never RestAssured.
> If the gap is only a field or two, I fix the Swagger DTO and regenerate
> instead. I keep a running list of every one of these so none of them get
> forgotten.
> * Coexistence was the right call for the first phase. It ends when criterion
> #4 lands.
> h2. PR roadmap
> I'm keeping the PR count deliberately low and putting the detail into commits
> instead. Each
> remaining PR is one area, broken into commits that each build and pass on
> their own, so they
> can be reviewed one at a time without the PR itself becoming unreviewable.
> ||PR||What||Files||Tests||Ticket||GitHub||Status||
> |1|Savings helper, test base, Swagger fields|—|—|FINERACT-2620, 2626|#5907,
> #5934|Merged|
> |2|Client helper build-out, Swagger fields|—|—|FINERACT-2631, 2632|#5954,
> #5955|Merged|
> |3|Charges and delinquency helpers|—|—|FINERACT-2635|#5978|Merged|
> |4|Loan helper: charges, transitions,
> disbursement|—|—|FINERACT-2640|#5997|Merged|
> |5|Loan tests, 4 tiers|—|—|FINERACT-2649|4 PRs, see ticket|Last tier in
> review|
> |6|Group and centre|3|16|FINERACT-2718|#6194|*In review — I'm here*|
> |7|Loan: money movement|21|360|to raise|—|Next|
> |8|Loan: lifecycle, product, COB|33|113|to raise|—|Not started|
> |9|Savings and deposits|20|209|to raise|—|Not started|
> |10|Client, charges, accounting|20|101|to raise|—|Not started|
> |11|Organisation, jobs, batch, config|33|228|to raise|—|Not started|
> |12|Closeout|—|—|to raise|—|Not started|
> |S1|Bulk import, shares, reporting, smoke tests|21|39|stretch|—|Not started|
> |S2|Split {{FeignLoanTestBase}}|—|—|stretch|—|Not started|
> |S3|The 50 Retrofit tests|50|—|stretch|—|Not started|
> PRs 7 to 11 add up to 127 files and 1,011 tests. S1 covers the last 21 files
> and 39 tests.
> h2. Log
> One line per merge. I don't edit old entries, only add to the bottom.
> ||Date||PR||Ticket||What went in||
> |2026-06-04|[#5934|https://github.com/apache/fineract/pull/5934]|FINERACT-2626|Added
> {{rejectedOnDate}} and {{withdrawnOnDate}} to the savings Swagger spec. The
> generated model was missing them, so a reject call silently did nothing.
> Needed before #5907 could land.|
> |2026-06-07|[#5907|https://github.com/apache/fineract/pull/5907]|FINERACT-2620|First
> area done end to end. 10 new files: savings helper, test base, lifecycle
> extension, request builders, and an 8-test lifecycle test to prove the
> pattern works.|
> |2026-06-09|[#5955|https://github.com/apache/fineract/pull/5955]|FINERACT-2632|Client
> Swagger fields the server accepts but the model didn't have:
> {{{}submittedOnDate{}}}, the withdrawal fields, and
> {{{}firstname{}}}/{{{}lastname{}}} on PUT.|
> |2026-06-09|[#5954|https://github.com/apache/fineract/pull/5954]|FINERACT-2631|{{FeignClientHelper}}
> from 4 methods to 16, plus {{ClientRequestBuilders}} and a full client
> lifecycle test.|
> |2026-06-11|[#5978|https://github.com/apache/fineract/pull/5978]|FINERACT-2635|Charges
> and delinquency: {{FeignChargesHelper}} (14 methods),
> {{{}ChargeRequestBuilders{}}}, and a 7-test delinquency lifecycle. Also fixed
> 4 fields missing from {{PostChargesRequest}} that were sending nulls on PUT.|
> |2026-06-21|[#5997|https://github.com/apache/fineract/pull/5997]|FINERACT-2640|Loan
> helper grew charge management, state transitions, disbursement details and
> reschedule: 11 charge methods, 6 transitions, 4 disbursement methods.|
> |from 2026-06-26|4 PRs|FINERACT-2649|The loan test migration, done in four
> tiers. Tier 4 is the big one at 92 files. Per-PR detail and review notes are
> on that ticket rather than duplicated here. Final tier is in review.|
> |2026-07-28|[#6194|https://github.com/apache/fineract/pull/6194]|FINERACT-2718|Group
> and centre. New helpers for group, centre, staff and GLIM. Migrated
> {{{}CenterIntegrationTest{}}}, {{GroupTest}} and {{GroupLoanIntegrationTest}}
> (16 tests) and retired a raw-HTTP stopgap helper that was Feign in name only.
> No changes to the shared loan test base. In review.|
--
This message was sent by Atlassian Jira
(v8.20.10#820010)