Introduction to Property-Based Testing Using a Money-Transfer Example 🏦💰
Have you ever wondered how banks make sure that when you transfer money, the right amount always ends up in the right account? One of the ways they do this is using something called “Property-Based Testing”.
In this scenario, imagine two banks, Bank A and Bank B. We are testing a system where people can transfer money from Bank A to Bank B, and we want to ensure that the total amount of money in both banks stays the same, even if something goes wrong during the transfer!
Here’s how Property-Based Testing works in this example:
-
Defining Properties: We start by defining some rules (properties) that should always be true. For our banks, the properties are:
- Consistency: The total amount of money in both banks should stay the same.
- No Money Loss: The money should either stay in Bank A or be transferred to Bank B.
-
Generating Test Cases: Instead of writing specific scenarios, we let the computer generate random test cases for us. In our example, it randomly decides the amount of money to transfer.
-
Checking Properties: For each test case, we check if our properties hold. In our example, we simulate transferring money and check whether the total amount remains consistent, and no money is lost.
-
Reporting Errors: If we find any scenario where the properties don’t hold, the test fails, and we know there’s a problem in our system!
By using this method, we can test our money-transfer system with a wide variety of scenarios, making sure it works correctly, even when things don’t go as planned!
Testing a Money-Transfer System 🏦💰
Imagine you have two banks, Bank A and Bank B. You are testing a system where people can transfer money from Bank A to Bank B.
The Code 🧑💻
const fc = require('fast-check');
const initialAmountA = 1000;
const initialAmountB = 1000;
fc.assert(
fc.property(fc.integer(1, 1000), async (transactionAmount) => {
let amountA = initialAmountA;
let amountB = initialAmountB;
amountA -= transactionAmount;
if (Math.random() < 0.9) {
amountB += transactionAmount;
}
const totalAmount = amountA + amountB;
const initialTotalAmount = initialAmountA + initialAmountB;
if (totalAmount !== initialTotalAmount) {
throw new Error('Consistency property violated');
}
if (amountB === initialAmountB && amountA !== initialAmountA - transactionAmount) {
throw new Error('No Money Loss property violated');
}
})
);
Breaking It Down 🧐
What are we doing?
We are testing if the money-transfer system works correctly, even when some transactions fail.
How does the code work?
Setting Up:
- initialAmountA and initialAmountB: These are the starting amounts in both banks.
Testing:
- fc.assert: This function checks if our test (the things we want to make sure work correctly) passes or fails.
- fc.property: This is our test! It runs multiple times with different transactionAmounts.
Simulating Transactions:
- amountA -= transactionAmount: We take away the transfer amount from Bank A.
- if (Math.random() < 0.9): We flip a virtual coin to decide if Bank B gets the money (90% chance it does).
Checking the Rules (Properties):
- Consistency: We check if the total money is still the same.
- No Money Loss: We check if the money is either still in Bank A or has reached Bank B.
What Happens if Something Goes Wrong?
- If any rule is broken, the code throws an error, telling us something is wrong!
Conclusion 🌟
And that’s how we make sure our money-transfer system works well, even when things don’t go as planned!