Core Banking & Ledger Testing
Under every account balance you see in a banking app sits a ledger. It is the bank’s single source of truth about who has what, and it must always balance. This lesson teaches you to test it.
1 The Hook
A fictional NZ bank, Aotearoa Mutual, ran a routine overnight interest run. The job credited interest to every savings account, then moved on. The next morning the bank’s general ledger did not balance. The total of all customer balances was higher than the total the bank actually held by a few thousand dollars. Small money for a bank. A red alert all the same.
The cause was a single account. One customer’s record had been migrated from an old system with a corrupt interest rate field. The interest run credited that account but, because of how the corrupt field flowed through, never recorded the matching debit against the bank’s interest expense account. Money appeared on one side of the ledger with nothing on the other side to balance it.
In normal software a value being slightly off is a bug you fix next sprint. In a ledger it is a different category of problem. The ledger is the bank’s claim about reality, and reality has to add up. If the two sides do not match, the bank cannot trust any number it reports — to a customer, to the RBNZ, or to its own auditors. The bank halted the run and traced it by hand.
Here is the lesson hidden in that story. The interest calculation worked. The amount was correct. What failed was the rule that every credit must have an equal and opposite debit. The testers had checked that interest was calculated correctly. No one had a test that asserted the ledger still balanced after the run. That assertion is the heart of core banking testing.
2 The Rule
In a core banking system the ledger is the source of truth, and the ledger must always balance. Every transaction is two movements — a debit and an equal credit — and the sum of all of them is always zero. Your most important test is not that an amount is correct. It is that after every operation, debits still equal credits and the system still ties out.
3 The Analogy
The till at a Four Square at the end of a shift.
When the shop closes, the person on the till counts the cash drawer and checks it against the day’s sales. The cash in the drawer must equal the opening float plus everything sold minus everything refunded. If the drawer is twenty dollars short, you do not shrug and lock up. Something happened — a wrong key, a missed refund, a note in the wrong slot — and you find it before you go home, because the count has to tie out.
A banking ledger is that till count, run continuously across millions of accounts. Every movement of money has a source and a destination, and the total must always reconcile. A tester in core banking is the person who refuses to lock up while the drawer is short. The job is to prove the count ties out after every transaction, not just to check that one sale rang up the right price.
4 Accounts, Balances and Postings
Three terms sit at the base of core banking. Get them precise and the rest follows.
An account is a record that holds money or tracks an obligation. A customer’s everyday account is one. But the bank itself holds many internal accounts the customer never sees — an interest expense account, a fees income account, a suspense account for items it cannot yet place. Every dollar lives in some account.
A posting is a single entry that moves value into or out of an account. A posting is either a debit or a credit. The words are counter-intuitive: in a bank’s books, a credit to a customer’s deposit account increases what the bank owes the customer, so it increases the customer’s balance. Do not rely on the everyday meaning of the words — rely on the direction of the movement and whether the account is an asset or a liability to the bank.
A balance is the running total of all postings to an account. Critically, a balance is derived, not stored as the truth. The truth is the sequence of postings; the balance is what you get when you add them up. This matters for testing: if a displayed balance disagrees with the sum of postings, the postings win and the display is the bug.
There is also a difference between the available balance and the ledger balance. The ledger balance is what has been fully posted. The available balance is what the customer can actually spend right now — the ledger balance minus holds, pending card authorisations, and uncleared items. A customer ringing the Banking Ombudsman because their balance “said” they had money is very often a confusion between these two. Test both, and test that the difference between them is exactly the holds and pending items, nothing more.
5 Double-Entry and Why It Must Balance
Double-entry bookkeeping is five centuries old and it is still the rule every core banking system obeys. Every transaction posts to at least two accounts. The total debited always equals the total credited. Money is never created or destroyed inside the system — it only moves from one account to another.
Take a simple transfer. A customer at Kiwibank moves $200 from their everyday account to their savings account. The system posts a debit of $200 to the everyday account and a credit of $200 to the savings account. Two postings, equal and opposite. The customer’s total holding is unchanged; the money has only moved.
Now take interest, the Aotearoa Mutual case. The bank pays a customer $5 interest. It credits $5 to the customer’s account — the bank now owes the customer more. But that $5 has to come from somewhere in the books, so the bank also debits $5 to its interest expense account. Two postings again, equal and opposite. If only the customer credit posts and the expense debit does not, the ledger is out of balance — exactly the failure in the Hook.
For a tester this gives you a single, powerful invariant to assert after every operation: the sum of all debits equals the sum of all credits. It must hold after one transaction, after a batch run, and across the whole system at end of day. An operation that breaks this invariant is wrong even if every individual amount looks correct. This is the assertion the Aotearoa Mutual testers were missing.
6 Settlement and Reconciliation
A balanced ledger inside one bank is only half the picture. Money also moves between banks, and that is where settlement and reconciliation come in.
Settlement is the actual movement of funds between banks to make good on the payments their customers send each other. When a Westpac NZ customer pays a BNZ customer, the two banks owe each other the net of all such payments. In NZ these interbank obligations are ultimately settled across accounts the banks hold at the RBNZ, through the Exchange Settlement Account System. A tester does not run settlement, but you do test the systems that feed it: that the amounts a bank reports it owes are correct, complete, and produced before the cut-off time.
Reconciliation is the act of proving two independent records agree. The bank’s internal ledger says it sent a certain set of payments for a certain total; the settlement system, or a payment scheme, says it received a certain set. Reconciliation matches the two, line by line, and flags anything that does not match — a payment the ledger shows but settlement does not, or a duplicate, or an amount that differs. An unreconciled item is not an accounting footnote. It is money that may be lost, duplicated, or stuck, and it is exactly what a tester should hunt for.
The cut-off matters as much as the amount. NZ’s traditional interbank payments run in scheduled exchanges through the day, so a file that is correct but late can miss its settlement window and leave funds in limbo until the next one. Newer real-time rails change the timing but not the principle: the system must produce the right total, complete, on time, and it must reconcile afterwards.
7 What to Test in a Ledger
Pulling the theory into a checklist, here is what a core banking tester actually targets.
- The balance invariant: after every transaction and every batch run, total debits equal total credits, system-wide. This is the non-negotiable assertion.
- Balance derivation: a displayed balance equals the sum of its postings. Available balance equals ledger balance minus holds and pending items — and nothing else.
- Rounding: interest, fees, and currency conversion produce fractions of a cent. Test that rounding is applied consistently and that the rounded amounts still tie out — a repeated half-cent error across a million accounts is real money.
- Value dating: a posting can be effective on a date different from when it was entered. Test that interest and balances are calculated against the correct value date, especially across weekends and public holidays.
- Failure and reversal: if a transaction fails halfway, no half-posted entry may survive. The whole transaction commits or none of it does. Test partial failure and confirm the ledger is untouched.
- Reconciliation: internal records match external settlement and scheme records, with every break flagged and explained.
- Cut-off timing: settlement and exchange files are produced complete and before their window closes.
8 Building Ledger Test Cases
A ledger test case has a shape that other functional test cases do not. The acceptance criterion is usually an equality — one total must equal another — and the evidence is the two totals side by side.
Here is a worked test case for a transfer on the Aotearoa Mutual core banking system:
Risk category: Ledger integrity (balance invariant)
Pre-conditions: Everyday account A holds $1,000.00; savings account B holds $500.00.
System-wide total debits = total credits (captured as baseline).
Action: Transfer $200.00 from account A to account B.
Expected result: A = $800.00; B = $700.00. A debit of $200.00 and a credit of
$200.00 are posted; no other accounts change.
Invariant check: System-wide total debits still equal total credits after the transfer.
Evidence required: Before/after balances for A and B; the two posting records; the
system-wide debit-total and credit-total before and after.
Traceability: Risk R-01 (one-sided posting breaks ledger balance).
Result: [Pass / Fail]
Notice three things. The acceptance criterion is an equality, not a single number. The invariant check is a separate, explicit line — the assertion the Hook story was missing. And the evidence is the totals themselves, captured before and after, so a reviewer can re-add them and confirm. That is a ledger test case rather than a casual balance check.
9 Common Mistakes
🚫 Testing the amount but never asserting the ledger still balances
Why it happens: Checking that $200 moved feels like the whole test, and it is the obvious part.
The fix: A correct amount can still leave a one-sided posting — the Aotearoa Mutual failure. Always add the invariant assertion: system-wide debits equal credits before and after every money-moving operation.
🚫 Using floating-point numbers for money in test data and assertions
Why it happens: It is the default numeric type, and small test amounts seem to work.
The fix: Floating point cannot represent many decimal cents exactly, so totals drift by fractions over many operations. Money is held in minor units (cents) or a fixed-precision decimal. Test with amounts that expose rounding, and assert exact equality, not “close enough”.
🚫 Confusing available balance with ledger balance
Why it happens: Both are “the balance” and the difference is invisible until a hold or pending item exists.
The fix: Test them separately. Ledger balance is what is posted; available balance is ledger balance minus holds and pending authorisations. Assert that the gap between them is exactly the holds — not a cent more or less.
🚫 Not testing partial failure and reversal
Why it happens: The happy path passes, and failure mid-transaction feels like an edge case.
The fix: A transaction that fails after posting one side but not the other leaves the ledger broken. Force a failure between the debit and the credit and confirm the whole transaction rolls back — the ledger must be exactly as it was before.
10 Now You Try
Three graded exercises on ledger integrity. Write your answer, run it for AI feedback, then compare to the model answer.
Read the description of a fictional fee-charging batch job for Kiwibank below. Identify 3 ledger-integrity risks that could leave the ledger out of balance or produce wrong balances, and explain why each matters.
An overnight job charges a $5 monthly fee to every eligible account. For each account it credits the bank’s fee-income account and debits the customer’s account. The fee is calculated as a percentage of average balance and stored as a floating-point number, then displayed rounded to cents. If an account is closed mid-run the job skips the customer debit but has already posted the fee-income credit. The job runs at 2am and writes balances as it goes; if it crashes partway it is simply re-run from the start the next night.
List 3 ledger-integrity risks and why each matters:
Show model answer
There are at least four real risks here; any three well-explained earns full marks. 1. One-sided posting on account closure — When an account closes mid-run, the job posts the fee-income credit but skips the customer debit. That is a credit with no matching debit, so the ledger no longer balances. This is the core double-entry violation. 2. Floating-point money — Storing the fee as a floating-point number means the fee-income credit and the customer debit can round differently, and many small errors accumulate across every eligible account. Money should be held in minor units or fixed-precision decimal, and the two sides must be exactly equal. 3. No safe re-run / not transactional — If the job crashes partway and is re-run from the start, accounts already charged could be charged twice, or partially-posted transactions could be left behind. Each fee should be one atomic transaction (both sides commit or neither), and the run should be idempotent so a re-run cannot double-charge. Bonus risk: the job writes balances as it goes with no end-of-run invariant check, so nobody asserts that total debits equal total credits after the batch — exactly the assertion that catches the closure bug. The trap: testing that "the $5 fee is correct" passes while every one of these defects ships, because the amount is right and only the invariant is wrong.
The ledger test case below is too weak to catch a balance defect. Rewrite it to be rigorous, with these fields: Test ID, Risk category, Pre-conditions, Action, Expected result, Invariant check, Evidence required, Traceability. Use a fictional BNZ overnight interest run as the context.
“Run the interest job. Check the customer got the right interest. Pass if the balance looks correct.”
Rewrite as a rigorous ledger test case:
Show model answer
Test ID: LDG-INT-012 Risk category: Ledger integrity (interest run must keep debits = credits) Pre-conditions: Savings account S holds $10,000.00. The bank's interest-expense account E has a known balance. System-wide total debits = total credits, captured as baseline. Interest rate and value date are set. Action: Run the overnight interest job for the value date. Expected result: Account S is credited the correctly calculated interest (rounded to cents per the bank's rounding rule). The interest-expense account E is debited the same amount. No other account changes. Invariant check: System-wide total debits still equal total credits after the run. The credit to S exactly equals the debit to E for this account. Evidence required: Before/after balances for S and E; the two posting records; the calculated-vs-posted interest amount showing the rounding; system-wide debit-total and credit-total before and after the run. Traceability: AI/risk register R-02 (interest run posts a one-sided entry and unbalances the ledger). What makes it rigorous: a measurable expected amount with the rounding rule, an explicit invariant check (debits = credits) as its own line, the matching debit named (not just "the customer got interest"), and reproducible evidence — the totals a reviewer can re-add. The original had none of these.
Design a reconciliation test plan of 5 test cases for a fictional Westpac NZ end-of-day interbank settlement file. Each test case needs at least: an ID, what it verifies, an acceptance criterion, and the evidence required. Cover matched totals, missing items, duplicates, amount mismatches, and cut-off timing.
Show model answer
RECON-01 | Verifies: the ledger's total of outbound payments matches the settlement file's total | Acceptance criteria: internal total equals settlement-file total to the cent; difference = 0 | Evidence required: both totals side by side; the date and snapshot they were taken from RECON-02 | Verifies: every internal payment appears in the settlement file (no missing items) | Acceptance criteria: 0 payments present in the ledger but absent from the file; any break listed with its reference | Evidence required: line-by-line match report; list of unmatched ledger items RECON-03 | Verifies: no payment appears twice (no duplicates) | Acceptance criteria: 0 settlement items matching more than one ledger item or vice versa | Evidence required: duplicate-detection report keyed on payment reference RECON-04 | Verifies: amounts agree for matched items | Acceptance criteria: for every matched pair, ledger amount equals file amount to the cent; 0 mismatches | Evidence required: matched-pair report showing amounts; list of any differing pairs RECON-05 | Verifies: the settlement file is produced complete and before the exchange cut-off | Acceptance criteria: file generated and submitted before the scheduled cut-off time; record count equals expected count | Evidence required: file timestamp vs cut-off time; expected-vs-actual record count Strong plans: each case is specific, has a measurable criterion (an equality or a zero-break count), names concrete evidence, and together they cover matched totals (RECON-01), missing items (RECON-02), duplicates (RECON-03), amount mismatches (RECON-04), and timing (RECON-05). Weak plans say "check the file reconciles" five times — that is the difference being marked.
11 Self-Check
Click each question to reveal the answer.
Q1: What is the single most important invariant to assert after any money-moving operation?
That system-wide total debits still equal total credits — the double-entry balance. Every transaction is a debit and an equal credit, so the two totals must always match. A correct amount with a one-sided posting still breaks this invariant, which is why it is the assertion you add to every money test.
Q2: Why is a displayed balance not the source of truth?
Because a balance is derived — it is the running sum of all postings to the account. The postings are the truth. If a displayed balance disagrees with the sum of its postings, the display is the bug, not the postings. Test that the balance equals the sum of its postings.
Q3: What is the difference between available balance and ledger balance?
Ledger balance is what has been fully posted. Available balance is what the customer can spend right now — ledger balance minus holds, pending card authorisations, and uncleared items. Test both, and assert the gap between them is exactly the holds and pending items, nothing more.
Q4: Why should money never be stored as a floating-point number?
Floating point cannot represent many decimal cents exactly, so totals drift by fractions over many operations and the two sides of the ledger stop matching exactly. Money is held in minor units (cents) or fixed-precision decimal, and you assert exact equality, not “close enough”.
Q5: What is reconciliation, and why does an unreconciled item matter?
Reconciliation proves two independent records agree — the bank’s internal ledger against the settlement or scheme record — matching line by line and flagging breaks. An unreconciled item is money that may be lost, duplicated, or stuck between banks, so it is exactly what a tester should hunt down rather than wave through.
12 Interview Prep
Real questions asked in NZ QA interviews for banking roles. Read the model answers, then practise your own version.
“You are testing a transfer feature. What do you check beyond the two account balances?”
Beyond confirming the source dropped by the amount and the destination rose by it, I assert the ledger invariant: a debit and an equal credit were posted, no other account moved, and system-wide total debits still equal total credits before and after. I also test partial failure — force a crash between the two postings and confirm the whole transaction rolls back so the ledger is untouched. And I check rounding and that available versus ledger balance behave correctly. The amount being right is the easy half; proving the books still balance is the half that catches the dangerous bugs.
“A customer says their balance showed $300 but their payment was declined. How do you investigate?”
My first hypothesis is the difference between ledger balance and available balance. The $300 was probably the ledger balance, but a hold or a pending card authorisation reduced what was actually available to spend, so the payment was correctly declined. I’d trace the account’s postings, list the holds and pending items, and confirm available balance equals ledger balance minus exactly those. If the gap is not explained by holds, then there is a real defect — a stale display or a balance that does not equal the sum of its postings — and I’d chase that.
“Why does cut-off time matter in settlement, and how would you test it?”
NZ interbank payments settle in scheduled windows, so a settlement file that is perfectly correct but produced after its cut-off misses the window and leaves funds in limbo until the next one — that is a real customer and liquidity impact, not just a late job. I’d test that the file is generated complete and submitted before the cut-off, with the expected record count, and I’d test the boundary: a run that finishes just before and just after the cut-off, to confirm the system handles the deadline correctly rather than silently sending late.