Skip to content

refactor(test): make tests deterministic#1242

Merged
Nihantra-Patel merged 15 commits into
frappe:developfrom
Nihantra-Patel:ci_lightmode_runner
Jun 1, 2026
Merged

refactor(test): make tests deterministic#1242
Nihantra-Patel merged 15 commits into
frappe:developfrom
Nihantra-Patel:ci_lightmode_runner

Conversation

@Nihantra-Patel

@Nihantra-Patel Nihantra-Patel commented May 26, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added a shared LendingTestSuite with automatic loan bootstrap and a workflow to run individual tests.
  • Bug Fixes

    • Excluded loan-linked records from applicant-wise shortfall lookups.
    • Improved detection of secured loans to consider pledged assignments.
  • Tests

    • Migrated 60+ test modules to the unified test suite.
    • Added broader ALM audit report tests and standardized test fixtures.
  • Chores

    • CI/workflow updates: version-16 branch handling, new skipped-tests workflow, test-run improvements, coverage gating, and backport rules.

@codecov-commenter

codecov-commenter commented May 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.57%. Comparing base (33bbb08) to head (b4e83a1).

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #1242      +/-   ##
===========================================
+ Coverage    81.12%   81.57%   +0.44%     
===========================================
  Files          152      153       +1     
  Lines        10531    10560      +29     
===========================================
+ Hits          8543     8614      +71     
+ Misses        1988     1946      -42     
Files with missing lines Coverage Δ
...type/bulk_repayment_log/test_bulk_repayment_log.py 100.00% <100.00%> (ø)
...octype/days_past_due_log/test_days_past_due_log.py 100.00% <100.00%> (ø)
.../doctype/lending_settings/test_lending_settings.py 100.00% <100.00%> (ø)
lending/loan_management/doctype/loan/test_loan.py 99.19% <100.00%> (-0.28%) ⬇️
...pe/loan_accrual_repost/test_loan_accrual_repost.py 100.00% <100.00%> (ø)
...nt/doctype/loan_adjustment/test_loan_adjustment.py 100.00% <100.00%> (ø)
.../doctype/loan_application/test_loan_application.py 100.00% <100.00%> (ø)
...balance_adjustment/test_loan_balance_adjustment.py 100.00% <100.00%> (ø)
...gement/doctype/loan_category/test_loan_category.py 100.00% <100.00%> (ø)
...pe/loan_classification/test_loan_classification.py 100.00% <100.00%> (ø)
... and 50 more

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Nihantra-Patel Nihantra-Patel marked this pull request as ready for review June 1, 2026 08:46
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 682a7e4d-435e-4fc1-bf75-5ee3a41781a6

📥 Commits

Reviewing files that changed from the base of the PR and between b4e83a1 and 580d64c.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml
  • .github/workflows/ci_faux.yml
💤 Files with no reviewable changes (1)
  • .github/workflows/ci_faux.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/ci.yml

Walkthrough

This PR comprehensively refactors the lending app's test infrastructure by introducing a custom LendingTestSuite base class that replaces Frappe's various test base classes across 60+ test files. It establishes a new bootstrap system (lending/tests/utils.py) that automatically initializes master test data on module import, eliminating repetitive setup in individual tests. Test data and assertions are adjusted to match new security parameters. Additionally, the ALM audit report gains new unit and integration tests, loan security shortfall logic is refined to prevent applicant-wise overwriting of loan-specific shortfalls, and CI/CD workflows are enhanced with version-16 branch support and per-module test execution capabilities.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main refactoring objective: making tests deterministic through systematic changes across the test suite.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
lending/loan_management/doctype/loan_disbursement/test_loan_disbursement.py (1)

16-20: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Trim redundant bootstrap calls in TestLoanDisbursement.setUp
lending/tests/test_utils.py instantiates BootStrapTestData() at import time and it already runs init_loan_products() and init_customers() (also sets _Test Company loan_accrual_frequency to "Monthly"; plus creates loan accounts + demand offset order). So in lending/loan_management/doctype/loan_disbursement/test_loan_disbursement.py (setUp, lines ~17-20), the extra init_loan_products() / init_customers() are redundant; master_init() only matters for the "Weekly" accrual frequency override—keep master_init() only if the test depends on weekly accrual, otherwise drop/limit it accordingly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lending/loan_management/doctype/loan_disbursement/test_loan_disbursement.py`
around lines 16 - 20, The setUp in TestLoanDisbursement currently calls
master_init(), init_loan_products(), and init_customers() redundantly; remove
the duplicate init_loan_products() and init_customers() calls and only keep
master_init() when this test actually requires the weekly accrual override
(loan_accrual_frequency = "Weekly"); if the test does not depend on weekly
accrual, drop master_init() entirely so setUp relies on the BootStrapTestData
pre-initialization instead. Ensure references to TestLoanDisbursement.setUp,
master_init(), init_loan_products(), and init_customers() are updated
accordingly.
lending/loan_management/doctype/sanctioned_loan_amount/test_sanctioned_loan_amount.py (1)

26-29: ⚠️ Potential issue | 🟠 Major

Remove duplicated product/customer bootstrap from TestSanctionedLoanAmount.setUp

BootStrapTestData() runs at lending/tests/utils.py import time (via LendingTestSuite), and it already calls init_loan_products() and init_customers(), so those two calls in setUp are redundant. Keep master_init() because it performs additional setup (create_loan_accounts(), setup_loan_demand_offset_order()) and sets the accrual frequency to "Weekly".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lending/loan_management/doctype/sanctioned_loan_amount/test_sanctioned_loan_amount.py`
around lines 26 - 29, Remove the redundant bootstrap calls in
TestSanctionedLoanAmount.setUp: delete the init_loan_products() and
init_customers() calls, leaving only master_init() in setUp, because
BootStrapTestData (triggered by LendingTestSuite at import time) already runs
init_loan_products and init_customers; ensure master_init() remains because it
performs additional necessary setup (create_loan_accounts,
setup_loan_demand_offset_order and accrual frequency).
🧹 Nitpick comments (2)
.github/workflows/run-individual-tests.yml (1)

131-141: ⚡ Quick win

Avoid expanding ${{ matrix.test }} directly inside the run shell.

${{ matrix.test }} is derived from repository file paths and is expanded inline into the shell on lines 133 and 141, which is the template-injection pattern flagged by static analysis. Pass it through env and reference the shell variable instead so its value can never be interpreted as shell code.

🔒️ Proposed fix using an env var
       - name: Run Tests
+        env:
+          MATRIX_TEST: ${{ matrix.test }}
         run: |
-          site_name=$(echo "${{matrix.test}}" | sed -e 's/.*\.\(test_.*$\)/\1/')
+          site_name=$(echo "$MATRIX_TEST" | sed -e 's/.*\.\(test_.*$\)/\1/')
           echo "$site_name"
           mkdir ~/frappe-bench/sites/$site_name
           cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config.json" ~/frappe-bench/sites/$site_name/site_config.json
           cd ~/frappe-bench/
           bench --site $site_name reinstall --yes
           bench --site $site_name install-app lending
           bench --site $site_name set-config allow_tests true
-          bench --site $site_name run-tests --module ${{ matrix.test }} --lightmode
+          bench --site $site_name run-tests --module "$MATRIX_TEST" --lightmode
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/run-individual-tests.yml around lines 131 - 141, The
workflow is currently expanding the GitHub Actions expression ${{ matrix.test }}
directly inside the shell which risks template-injection; instead add an env
entry (e.g., TEST_MODULE: ${{ matrix.test }}) to the job/step and use the shell
variable $TEST_MODULE everywhere in the step: replace usages of the inline
expression in the site_name extraction (sed expression), and in the bench
run-tests invocation, with the quoted shell variable "$TEST_MODULE" so the value
is treated as data not code; ensure all references (site_name derivation and
bench --site ... run-tests --module) use the new shell variable.
lending/loan_management/doctype/loan_adjustment/test_loan_adjustment.py (1)

21-29: 💤 Low value

Redundant bootstrap calls in setUp.

master_init(), init_loan_products(), and init_customers() are now also performed by BootStrapTestData (lending/tests/utils.py). They remain idempotent, so this is harmless, but note master_init() resets accrual frequency to "Weekly", overriding the bootstrap's "Monthly" — intentional only if these tests rely on weekly accrual. Otherwise this setUp can be dropped.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lending/loan_management/doctype/loan_adjustment/test_loan_adjustment.py`
around lines 21 - 29, The TestLoanAdjustment.setUp currently calls
master_init(), init_loan_products(), and init_customers() which duplicate
BootStrapTestData in lending/tests/utils.py and unintentionally reset accrual
frequency to "Weekly"; remove these redundant calls from
TestLoanAdjustment.setUp so the bootstrap from BootStrapTestData is used
instead, or if the test truly requires weekly accrual keep only master_init()
(or better: remove master_init() and explicitly set the accrual frequency to
"Weekly" in the test setup) — locate TestLoanAdjustment.setUp and update it
accordingly, referencing master_init, init_loan_products, init_customers, and
BootStrapTestData.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lending/tests/utils.py`:
- Around line 21-57: The module currently instantiates BootStrapTestData() at
import which triggers DB writes (create_loan_accounts,
set_loan_settings_in_company, create_loan_security_price) and a
frappe.db.commit() in make_master_data; remove the top-level BootStrapTestData()
call and instead expose a function (e.g., initialize_test_data or
BootStrapTestData.bootstrap()) that calls make_presets/make_master_data and
performs commits only when invoked from test setup; ensure frappe.db.commit() is
moved into that callable (or removed if tests handle transactions) so no DB
writes occur on import, and update tests to call the new function in their
before_tests/setup hooks.

---

Outside diff comments:
In `@lending/loan_management/doctype/loan_disbursement/test_loan_disbursement.py`:
- Around line 16-20: The setUp in TestLoanDisbursement currently calls
master_init(), init_loan_products(), and init_customers() redundantly; remove
the duplicate init_loan_products() and init_customers() calls and only keep
master_init() when this test actually requires the weekly accrual override
(loan_accrual_frequency = "Weekly"); if the test does not depend on weekly
accrual, drop master_init() entirely so setUp relies on the BootStrapTestData
pre-initialization instead. Ensure references to TestLoanDisbursement.setUp,
master_init(), init_loan_products(), and init_customers() are updated
accordingly.

In
`@lending/loan_management/doctype/sanctioned_loan_amount/test_sanctioned_loan_amount.py`:
- Around line 26-29: Remove the redundant bootstrap calls in
TestSanctionedLoanAmount.setUp: delete the init_loan_products() and
init_customers() calls, leaving only master_init() in setUp, because
BootStrapTestData (triggered by LendingTestSuite at import time) already runs
init_loan_products and init_customers; ensure master_init() remains because it
performs additional necessary setup (create_loan_accounts,
setup_loan_demand_offset_order and accrual frequency).

---

Nitpick comments:
In @.github/workflows/run-individual-tests.yml:
- Around line 131-141: The workflow is currently expanding the GitHub Actions
expression ${{ matrix.test }} directly inside the shell which risks
template-injection; instead add an env entry (e.g., TEST_MODULE: ${{ matrix.test
}}) to the job/step and use the shell variable $TEST_MODULE everywhere in the
step: replace usages of the inline expression in the site_name extraction (sed
expression), and in the bench run-tests invocation, with the quoted shell
variable "$TEST_MODULE" so the value is treated as data not code; ensure all
references (site_name derivation and bench --site ... run-tests --module) use
the new shell variable.

In `@lending/loan_management/doctype/loan_adjustment/test_loan_adjustment.py`:
- Around line 21-29: The TestLoanAdjustment.setUp currently calls master_init(),
init_loan_products(), and init_customers() which duplicate BootStrapTestData in
lending/tests/utils.py and unintentionally reset accrual frequency to "Weekly";
remove these redundant calls from TestLoanAdjustment.setUp so the bootstrap from
BootStrapTestData is used instead, or if the test truly requires weekly accrual
keep only master_init() (or better: remove master_init() and explicitly set the
accrual frequency to "Weekly" in the test setup) — locate
TestLoanAdjustment.setUp and update it accordingly, referencing master_init,
init_loan_products, init_customers, and BootStrapTestData.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 16e50846-91a2-430e-bb78-588396a70080

📥 Commits

Reviewing files that changed from the base of the PR and between 33bbb08 and b4e83a1.

📒 Files selected for processing (65)
  • .github/helper/install.sh
  • .github/workflows/ci.yml
  • .github/workflows/ci_faux.yml
  • .github/workflows/run-individual-tests.yml
  • .mergify.yml
  • lending/loan_management/doctype/bulk_repayment_log/test_bulk_repayment_log.py
  • lending/loan_management/doctype/days_past_due_log/test_days_past_due_log.py
  • lending/loan_management/doctype/lending_settings/test_lending_settings.py
  • lending/loan_management/doctype/loan/test_loan.py
  • lending/loan_management/doctype/loan_accrual_repost/test_loan_accrual_repost.py
  • lending/loan_management/doctype/loan_adjustment/test_loan_adjustment.py
  • lending/loan_management/doctype/loan_application/test_loan_application.py
  • lending/loan_management/doctype/loan_balance_adjustment/test_loan_balance_adjustment.py
  • lending/loan_management/doctype/loan_category/test_loan_category.py
  • lending/loan_management/doctype/loan_classification/test_loan_classification.py
  • lending/loan_management/doctype/loan_demand/test_loan_demand.py
  • lending/loan_management/doctype/loan_demand_offset_order/test_loan_demand_offset_order.py
  • lending/loan_management/doctype/loan_disbursement/test_loan_disbursement.py
  • lending/loan_management/doctype/loan_freeze_log/test_loan_freeze_log.py
  • lending/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py
  • lending/loan_management/doctype/loan_limit_change_log/test_loan_limit_change_log.py
  • lending/loan_management/doctype/loan_npa_log/test_loan_npa_log.py
  • lending/loan_management/doctype/loan_partner/test_loan_partner.py
  • lending/loan_management/doctype/loan_product/test_loan_product.py
  • lending/loan_management/doctype/loan_refund/test_loan_refund.py
  • lending/loan_management/doctype/loan_repayment/test_loan_repayment.py
  • lending/loan_management/doctype/loan_repayment_repost/test_loan_repayment_repost.py
  • lending/loan_management/doctype/loan_repayment_schedule/test_loan_repayment_schedule.py
  • lending/loan_management/doctype/loan_restructure/test_loan_restructure.py
  • lending/loan_management/doctype/loan_restructure_limit_log/test_loan_restructure_limit_log.py
  • lending/loan_management/doctype/loan_security/test_loan_security.py
  • lending/loan_management/doctype/loan_security_assignment/test_loan_security_assignment.py
  • lending/loan_management/doctype/loan_security_deposit/test_loan_security_deposit.py
  • lending/loan_management/doctype/loan_security_price/test_loan_security_price.py
  • lending/loan_management/doctype/loan_security_release/test_loan_security_release.py
  • lending/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
  • lending/loan_management/doctype/loan_security_shortfall/test_loan_security_shortfall.py
  • lending/loan_management/doctype/loan_security_type/test_loan_security_type.py
  • lending/loan_management/doctype/loan_transfer/test_loan_transfer.py
  • lending/loan_management/doctype/loan_write_off/test_loan_write_off.py
  • lending/loan_management/doctype/pledge/test_pledge.py
  • lending/loan_management/doctype/process_loan_classification/test_process_loan_classification.py
  • lending/loan_management/doctype/process_loan_demand/test_process_loan_demand.py
  • lending/loan_management/doctype/process_loan_interest_accrual/test_process_loan_interest_accrual.py
  • lending/loan_management/doctype/process_loan_restructure_limit/test_process_loan_restructure_limit.py
  • lending/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.py
  • lending/loan_management/doctype/process_loan_security_shortfall/test_process_loan_security_shortfall.py
  • lending/loan_management/doctype/sanctioned_loan_amount/test_sanctioned_loan_amount.py
  • lending/loan_management/report/alm_audit_report/alm_audit_report.py
  • lending/loan_management/report/alm_audit_report/test_alm_audit_report.py
  • lending/loan_management/report/applicant_wise_loan_security_exposure/test_applicant_wise_loan_security_exposure.py
  • lending/loan_management/report/future_cashflow_report/test_future_cashflow_report.py
  • lending/loan_management/report/loan_outstanding_report/test_loan_outstanding_report.py
  • lending/loan_management/report/loan_repayment_and_closure/test_loan_repayment_and_closure.py
  • lending/loan_management/report/loan_security_exposure/test_loan_security_exposure.py
  • lending/loan_management/report/loan_security_ledger/test_loan_security_ledger.py
  • lending/loan_management/report/loan_security_status/test_loan_security_status.py
  • lending/loan_management/report/loan_statement_of_account/test_loan_statement_of_account.py
  • lending/loan_management/report/past_cashflow_report/test_past_cashflow_report.py
  • lending/loan_origination/doctype/loan_document_type/test_loan_document_type.py
  • lending/loan_origination/doctype/loan_lead/test_loan_lead.py
  • lending/loan_origination/doctype/loan_origination_settings/test_loan_origination_settings.py
  • lending/loan_origination/doctype/loan_purpose/test_loan_purpose.py
  • lending/tests/test_utils.py
  • lending/tests/utils.py

Comment thread lending/tests/utils.py
Comment on lines +21 to +57
class BootStrapTestData:
def __init__(self):
self.make_presets()
self.make_master_data()

def make_presets(self):
create_loan_accounts()
setup_loan_demand_offset_order()

def make_master_data(self):
set_loan_settings_in_company()
set_loan_accrual_frequency("Monthly")
init_loan_products()
self.make_flat_interest_rate_loan()
create_loan_security_type()
create_loan_security()
create_loan_security_price(
"Test Security 1", 500, "Nos", nowdate(), add_days(nowdate(), 1), update_if_existing=True
)
create_loan_security_price(
"Test Security 2", 250, "Nos", nowdate(), add_days(nowdate(), 1), update_if_existing=True
)
init_customers()
make_customer("_Test Loan Customer 2")
frappe.db.commit() # nosemgrep

def make_flat_interest_rate_loan(self):
create_loan_product(
"Flat Interest Rate Loan",
"Flat Interest Rate Loan",
1000000,
12,
repayment_schedule_type="Flat Interest Rate",
)


BootStrapTestData()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🌐 Web query:

In Frappe framework test runner, does the before_tests hook run before or after test modules are imported during test discovery?

💡 Result:

before_tests runs before the test modules (test files) are imported during test discovery. Evidence: Frappe’s hook documentation describes before_tests as “Test Hooks” that run “before tests are run on a site,” and the unit-testing docs note there is a “test initialization script” that can be skipped via --skip-before-tests, implying the initialization (including before_tests) happens ahead of the actual test execution/import/collection phase. [1][2][3]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
ls -la
sed -n '1,120p' lending/tests/utils.py | cat -n

Repository: frappe/lending

Length of output: 3568


Avoid DB writes + frappe.db.commit() at module import time in lending/tests/utils.py

BootStrapTestData() is instantiated at import (line 57), which runs create_loan_accounts/set_loan_settings_in_company/create_loan_security_price and then frappe.db.commit() (line 45) during test discovery/import. Even if before_tests runs earlier, this still creates unwanted side effects when the module is merely imported (e.g., skipped/filtered runs).

♻️ Suggested change
-BootStrapTestData()
-
-
 class LendingTestSuite(ERPNextTestSuite):
 	"""Class for creating Lending test records"""
 
-	pass
+	`@classmethod`
+	def setUpClass(cls):
+		super().setUpClass()
+		BootStrapTestData()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lending/tests/utils.py` around lines 21 - 57, The module currently
instantiates BootStrapTestData() at import which triggers DB writes
(create_loan_accounts, set_loan_settings_in_company, create_loan_security_price)
and a frappe.db.commit() in make_master_data; remove the top-level
BootStrapTestData() call and instead expose a function (e.g.,
initialize_test_data or BootStrapTestData.bootstrap()) that calls
make_presets/make_master_data and performs commits only when invoked from test
setup; ensure frappe.db.commit() is moved into that callable (or removed if
tests handle transactions) so no DB writes occur on import, and update tests to
call the new function in their before_tests/setup hooks.

@Nihantra-Patel Nihantra-Patel merged commit 4fe61ec into frappe:develop Jun 1, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants