An online banking specification can be 100 pages long, carefully reviewed, signed off by several teams, and still leave some of the most important testing questions unanswered.
That is not necessarily because the specification is bad.
Specifications are usually written to describe expected behaviour: what a user can do, which fields exist, which permissions apply, how a payment moves through a workflow, which limits are enforced.
Testing has to look at the same system from a different direction. What happens when two valid actions collide? What happens when the state of an account changes halfway through a process? What happens when the backend succeeds but the frontend never gets the message? What happens when permissions change after a payment has already been scheduled? And, perhaps more importantly, which of those failures could actually cause serious damage?
That distinction matters.
A banking application can have hundreds or thousands of possible test scenarios. Treating all of them as equally important produces enormous test suites without necessarily telling you whether the most dangerous parts of the system are actually covered.
This is the problem we built Leonidas, our risk assessment tool, to help solve. Leonidas reads software specifications, extracts the functions described in them, and helps assess where risk is concentrated based on factors such as potential damage and usage frequency.
Instead of starting with:
How many test cases can we generate from this specification?
you start with:
Where can this system hurt us most if something goes wrong?
To show what that looks like in practice, here are 10 risks that can easily hide inside an online banking specification.
1. The balance changes while a payment is being made
Imagine the specification contains a requirement like this: The customer may initiate a bank transfer when sufficient funds are available.
It sounds clear. A conventional set of test cases might be:
- sufficient funds → payment succeeds
- insufficient funds → payment is rejected
- exact balance → expected boundary behaviour occurs
Useful tests. But they still assume that the account balance stays still while the user completes the transaction. Real banking systems do not have that luxury. A customer might have €1,000 available when they begin a €900 transfer.
Before the transfer is executed:
- a card payment settles,
- a direct debit is processed,
- another transfer is initiated,
- a joint account holder withdraws money,
- a pending transaction becomes final,
- or a fee is charged.
Suddenly, the balance that was sufficient at the beginning of the flow is no longer sufficient.
Now the important questions begin. When exactly should the balance be checked? At transfer creation? At authentication? At final confirmation? At execution? Should the bank reserve funds as soon as the customer starts the transfer? Can the account temporarily go below zero? If overdrafts are permitted, which limits apply?
What happens to the transaction status if there are enough funds during confirmation but not during execution? And what does the customer see?
This is a good example of why requirements and risks are not the same thing. The requirement may correctly describe the intended functionality while leaving several business-critical failure modes implicit.
How Leonidas helps
Leonidas does not simply need to recognise that there is a function called bank transfer. It can assess the function in terms of the damage caused by failure and how frequently users depend on it.
For example, a balance validation failure during payment execution has much higher business impact than a cosmetic issue on the transfer confirmation screen.
That distinction helps teams move beyond saying: We tested the payment functionality.
Towards: We tested the highest-risk failure modes inside the payment functionality.
2. The same payment is submitted twice
The customer enters a beneficiary. They type €2,500. They click Send. Nothing appears to happen. So they click again. This is a tiny moment from the user's perspective. From the bank's perspective, it can be a serious transactional problem.
The frontend may send the same request twice. A mobile connection may drop immediately after the first request reaches the server. The customer may refresh the page. The app may retry automatically.
A third-party payment service may resend a request because it never received a confirmation.
Now the banking platform has to determine whether it is seeing two legitimate transfers or the same transfer twice. A specification may describe the transfer process beautifully without explicitly defining duplicate protection.
That creates questions such as:
- Is every transfer assigned an idempotency key?
- Can identical transfers be executed within seconds of each other?
- How long does duplicate detection remain active?
- Does the system compare amount, beneficiary and timestamp?
- What if the customer genuinely wants to send two equal payments?
- What happens when the backend processes the first transaction but the frontend reports an error?
- Can an automatic retry result in another transfer?
The happy path does not reveal any of this. Yet the potential damage is obvious. A duplicated €5 payment is inconvenient. A duplicated €50,000 corporate payment can become an incident.
Risk is not the same as feature complexity
This is important when prioritising tests. Duplicate payment prevention might involve a relatively small part of the system. Its code footprint might be smaller than that of the transaction history screen. But its risk contribution can be significantly larger.
This is exactly why counting requirements, functions or test cases can be misleading. Leonidas helps surface functions where the consequence of failure deserves disproportionate attention.
3. Daily transfer limits behave differently at the edges
Suppose the specification says: The customer has a daily transfer limit of €10,000.
It looks like one requirement. It is not one scenario. Imagine the customer has already transferred €9,500 and now attempts another €600. Should it fail? Probably.
Now make the situation slightly more complicated. The first payment is still pending. Does it count toward the limit? What if it is later rejected? What about a scheduled payment created today but executed tomorrow? What about an internal transfer between the customer's own accounts?
What happens at 23:59? Which timezone determines when the daily limit resets? If the customer is travelling abroad, does anything change? What if the customer increases their limit at 23:55 and immediately makes another transfer? What if a user changes the limit while another session is open?
The phrase daily transfer limit contains assumptions about time, state, transaction type and synchronisation. Specifications often define the rule but not every state around the rule.
Why this matters for risk-based testing
The interesting testing question is not simply: Does the €10,000 limit work?
It is: Which ways of breaking this rule could result in financial loss, fraud exposure or regulatory problems?
That is the level where risk assessment becomes useful.
A €10,001 payment being blocked is obvious.
Two simultaneous €6,000 payments both being accepted because the system checks the old daily total before either is committed is much more interesting.
And much more dangerous.
4. The payment succeeds, but the customer thinks it failed
Not every dangerous banking defect means that the payment failed. [-Sometimes the payment works perfectly. The message telling the customer that it worked does not.
Imagine this sequence:
- The customer sends €2,000.
- The backend accepts the transaction.
- The transfer is recorded successfully.
- The confirmation response times out.
- The customer sees an error.
From the customer's perspective, the transfer failed. So they try again.
You may now have a duplicated payment even though the original transfer service performed exactly as designed.
This is why testing distributed systems solely through individual service responses can create false confidence.
The transaction service might be correct. The API might be correct. The database might be correct. The customer journey is still unsafe. A specification should therefore make transaction states explicit.
For example:
- initiated
- received
- processing
- confirmed
- rejected
- failed
- unknown
- reversed
That unknown state is particularly important. There are situations where the application genuinely does not yet know whether a payment succeeded. Pretending otherwise can be dangerous.
What Leonidas changes here
A traditional test inventory might contain separate tests for:
- transfer creation,
- API response,
- confirmation page,
- transaction history.
Leonidas helps you look at the business function across those technical boundaries. The risk is not merely “confirmation page error”. The risk is: The customer receives incorrect information about the state of a financial transaction and repeats it.
That is a far more useful description for deciding how thoroughly the scenario should be tested.
5. An authentication code expires at exactly the wrong moment
Strong authentication is usually described in specifications with fairly clear rules. A customer enters their credentials. A one-time password is generated. The code is valid for a limited period. Correct codes are accepted. Incorrect codes are rejected.
After several failed attempts, additional controls may apply. The obvious test cases are easy to create. The risks appear around timing and state transitions.
What happens when a code expires one second before submission? What if it expires while the server is processing the request? What happens when the customer requests another code? Does the first one stop working immediately? Can two codes briefly be valid at the same time?
If the user opens two browser tabs, can both continue with separate authentication sessions?
If an OTP is used to approve a payment, is it tied to the payment details? Could the same authentication token approve a different amount? What happens if the beneficiary changes after authentication begins?
These are not simply login tests anymore. They involve authentication, transaction integrity and fraud risk.
Frequency versus damage
Leonidas can help make another important distinction here. Some scenarios are unlikely to happen frequently but would be extremely damaging if they did.
Others occur constantly but have limited impact. Risk-based testing needs both dimensions. A failure involving an expired OTP might occur relatively rarely. A failure allowing an authentication token to approve a modified transaction could have enormous consequences.
Those scenarios should not automatically receive equal attention just because they sit under the same requirement.
6. A scheduled payment executes after the situation has changed
Scheduled payments are deceptively simple. The customer creates a payment today. The bank executes it next Friday. Between those two moments, almost anything relevant to the transaction can change.
The account may be frozen. The available balance may decrease. The beneficiary may be removed. The customer may change the transfer limit. An administrator may revoke the user's permissions. The beneficiary's account may become invalid. The customer may close the account. Compliance controls may flag the payment.
So which rules are evaluated when the customer schedules the transfer? And which rules need to be checked again when it executes? Consider something as basic as permissions.
A company employee with payment rights schedules a €20,000 transfer for next week. Two days later, their access is revoked. Should the payment still execute?
There is no universally obvious answer. It depends on the bank's rules. The risk is that the specification might never explicitly say.
Specifications describe moments. Banking systems live through time.
This is one of the recurring patterns in complex financial software.
Requirements often describe an action at a particular moment.
Risk appears in what happens between moments.
Leonidas can help expose functions with high dependency on delayed execution, changing states and business rules, allowing testers to ask those questions before they become production incidents.
7. A user can still access something after their permission is revoked
Permission testing is often treated too narrowly. An administrator removes a user's account access. The user logs in again. They can no longer see the account. Test passed. Except the user already had another browser session open.
Or the mobile app cached account data. Or they created a payment before their access was removed. Or they are still listed as an approver in an active transaction workflow. Or a previously issued API token still has access. Or the user opened the account overview before their permission changed and then tries to perform an action from the old page.
The real question is not:
Can access be revoked?
It is:
Does revocation propagate everywhere it needs to?
For business banking especially, there may be several permission layers:
- account visibility,
- payment creation,
- payment approval,
- beneficiary management,
- export permissions,
- administrator access,
- user management.
A permission change in one layer can have consequences elsewhere.
The hidden risk is often downstream
This is another reason simply converting each requirement into a test case can miss important behaviour.
A permission requirement may sit in one section of a specification.
Its consequences may affect ten other functions.
A risk assessment should capture that wider impact.
Leonidas helps teams identify functions whose failures have high downstream damage, rather than looking only at the local requirement.
8. Two users act on the same transaction at the same time
Business banking platforms often use maker-checker or multi-approval workflows. One user creates a payment. Another approves it.
Large transfers may require two or more approvals. On paper, the process looks linear. Software users are not linear. Imagine two approvers both have the same €100,000 transaction open.
User A approves it. User B approves it almost simultaneously. That may be perfectly valid. Now imagine that someone edits or cancels the transaction between those two actions.
Which version did User B approve? What if both users approve at exactly the same time?
What if the transaction reaches the required number of approvals while another request is still being processed? Can it execute twice? What happens if an approver's authority changes while the approval screen is already open? Can a user approve a transaction after their approval limit has been reduced?
Concurrency bugs are notoriously difficult because normal manual testing often performs actions one after another. Production systems do not.
A function can work perfectly in isolation and still fail under concurrency
This is why Leonidas should not be thought of as just a way of generating a priority list of screens.
The purpose is to assess business functions and their potential failure impact.
An approval workflow involving large financial transactions may deserve intensive testing of concurrent states even if those states represent a relatively small proportion of normal user behaviour.
9. The exchange rate changes between review and execution
International payments introduce another moving state: currency. A customer wants to send $5,000 from a euro account.
The application shows them:
- the exchange rate,
- the estimated euro amount,
- the transaction fee,
- and the final amount.
They review it. They authenticate. They confirm.
Meanwhile, the exchange rate changes. Which rate should apply? The one shown when the transfer was created? The one shown at confirmation? The rate at the exact moment the bank executes the transaction? How long is a quoted rate valid?
If the quote expires during authentication, should the user be sent back to the review screen? How large can the difference be before the customer must confirm again? What happens if the displayed amount and the booked amount differ?
Even the wording used by the interface becomes important.
There is a large difference between: You will pay €4,280 and: Estimated amount: €4,280
If the specification does not make this clear, both implementation and testing can end up relying on assumptions.
Financial risk is not always a system crash
Many of the highest-impact problems in financial software are not dramatic technical failures. The application works. The server stays online. The transaction processes.
But the financial outcome differs from what the customer reasonably expected. Risk-based testing needs to include those failures too.
10. Every individual feature passes, but the banking journey still fails
This may be the most important one.
Imagine you test these functions separately:
- login,
- account overview,
- beneficiary creation,
- payment creation,
- authentication,
- payment confirmation,
- transaction history.
Every one passes. You now have excellent functional coverage. But consider the complete journey: The user logs in. They create a new beneficiary. They make a €3,000 transfer.
They approve it using an OTP. They receive a success message. They open transaction history. The transfer is shown as failed.
Or perhaps it appears twice. Or the balance is not updated. Or the amount is correct but the beneficiary name is wrong. Or the payment remains pending indefinitely.
No individual feature necessarily failed in isolation. The contract between them did. This is where teams can have thousands of passing tests and still ship something risky.
Test coverage is not automatically risk coverage
This distinction is fundamental.
Imagine a suite contains 5,000 tests.
4,000 of them cover low-impact functionality.
900 cover moderate-risk behaviour.
Only 100 touch the functions that could create the most serious financial or operational damage.
The suite can have fantastic execution statistics while still leaving major business risk exposed.
That is why number of tests is a weak proxy for safety.
The more useful question is:
How much of the application's business risk is actually covered by those tests?
That is the question Leonidas is designed around.
From specifications to a risk model
A software specification contains a lot of information. Functions. Actors. Rules. Dependencies. Restrictions. States. Thresholds. Exceptions.
But it usually does not arrive neatly labelled:
HIGH-RISK FUNCTION. TEST THIS FIRST.
Someone still needs to interpret it. Traditionally, that process involves workshops, spreadsheets, domain experts and a lot of manual reading.
And it is valuable work. But it is also time-consuming, and large specifications make it easy for important functions to disappear inside hundreds of pages of detail. Leonidas helps make that first pass much faster.
You upload a specification. Leonidas identifies the functions described in the document and builds a structured view of the risks around them.
Those functions can then be assessed based on factors such as potential damage and frequency of use. The result is not merely another list of requirements. It is a way to see where risk is concentrated.
For an online banking system, for example, functions such as payment execution, authentication or approval workflows are likely to carry very different consequences from things such as sorting transaction history or changing a dashboard preference.
All of them may deserve tests. They do not necessarily deserve the same testing effort.
What Leonidas might see in an online banking specification
Suppose a fictional banking specification contains these functions:
| Function | Possible failure | Potential consequence |
| Execute transfer | Transfer executed twice | Direct financial loss |
| Check available balance | Payment accepted without sufficient funds | Overdraft or financial exposure |
| Approve corporate payment | Unauthorised approval accepted | Fraud or major financial loss |
| Authenticate customer | Invalid user gains access | Account compromise |
| Schedule payment | Payment executes after permissions change | Unauthorised transaction |
| Convert currency | Incorrect rate applied | Financial discrepancy |
| Display transaction status | Incorrect status shown | Customer repeats or disputes payment |
| Download statement | File formatting issue | Limited operational inconvenience |
| Sort transaction list | Sorting incorrect | Minor usability issue |
If you simply counted functions, each row would be one function. Eight functions. Eight things to test. But they obviously do not represent eight equal risks.
A broken sorting option and an unauthorised €100,000 transfer do not belong in the same priority bucket. Leonidas makes this distinction explicit. And once the functions are ranked by risk, test design becomes much more deliberate.
Risk first, test cases second
There is another useful consequence. Generative AI has made producing test cases extremely fast.
Give a model a requirement and it can produce:
- 10 test cases,
- 50 test cases,
- 200 test cases.
That is no longer the difficult part. The difficult part is deciding which scenarios deserve those tests in the first place. Otherwise, teams risk generating larger and larger suites around whatever happens to be written most clearly in the specification.
The biggest risk might be hidden in one ambiguous sentence.
That is why our preferred sequence is:
1. Understand the functions.
What can the system do?
2. Understand the risks.
What happens if those functions fail?
3. Prioritise them.
Which failures combine meaningful likelihood with serious damage?
4. Design the test coverage.
What needs to be tested to reduce those risks?
5. Automate where it makes sense.
Only then does test volume become useful.
Leonidas sits at the beginning of that process. It helps turn a specification into a view of the system based on business risk rather than document structure.
One sentence can hide ten test scenarios
This is perhaps the most useful habit when reviewing banking requirements. When you see: The user can transfer funds to another account. do not stop at the function.
Ask:
- What if they click twice?
- What if the balance changes?
- What if their permission disappears?
- What if the response times out?
- What if two sessions do it simultaneously?
- What if the beneficiary changes?
- What if the daily limit is reached during processing?
- What if the transfer succeeds but the interface says it failed?
- What if the transaction is scheduled for later?
- What if another system involved in the payment is unavailable?
That is where the real test design starts. And in banking, those questions matter because the consequences are not theoretical.
They can mean duplicated transfers, unauthorised access, incorrect balances, failed payments, fraud exposure, customer complaints and regulatory incidents.
A 200-page specification is not the same as 200 pages of risk
The safest-looking requirement document is not necessarily the safest system. Nor is the largest regression suite necessarily the best protected one. You can have thousands of requirements. Thousands of automated tests. Thousands of green checkmarks.
And still leave the highest-risk 5% of the application poorly covered.
That is the gap Leonidas is built to make visible. Upload the specification. Identify the functions. Score the risks. Then build your testing strategy around what can actually hurt the business.
Because the interesting question is no longer: How many test cases can we produce?
It is: If this banking application breaks tomorrow, where would we really not want it to break?

.png&w=3840&q=75)

