In a previous post, I explained property based testing. In this post we’ll see a simple example using fast-check
Let assume we’re building the same bank transfer code as described in the dotnet FsCheck earlier post. Here’s the TypeScript version of the test:
import BuggyBank from "../buggy-bank"
import * as fc from 'fast-check';
function transferProperties(startBalanceA: bigint
, startBalanceB: bigint
, amount: bigint) : void {
const bank = new BuggyBank()
bank.balanceOfA = startBalanceA
bank.balanceOfB = startBalanceB
try {
bank.transfer(amount)
} catch {
//Transfer failed
const balanceAUnchanged = bank.balanceOfA == startBalanceA
const balanceBUnchanged = bank.balanceOfB == startBalanceB
expect(balanceAUnchanged).toBeTruthy()
expect(balanceBUnchanged).toBeTruthy()
return
}
//Transfer succeeded
const balanceAIsNonNegative = bank.balanceOfA >= 0
const balanceAChanged = bank.balanceOfA != startBalanceA
const balanceBChanged = bank.balanceOfB != startBalanceB
expect(balanceAIsNonNegative).toBeTruthy()
expect(balanceAChanged).toBeTruthy()
expect(balanceBChanged).toBeTruthy()
}
test('properties of a bank transfer must be correct', () => {
const config = { numRuns : 10000 }
//use this if you need to control the seed for the random number generator
//const config = { numRuns : 10000, seed:123 }
const property = fc.property(fc.bigIntN(32)
, fc.bigIntN(32)
, fc.bigIntN(32)
, transferProperties)
fc.assert(property,config)
})
I created the quick-n-dirty example like this:
mkdir fast-check-example
cd fast-check-example
npm init --yes
npm install typescript ts-node
echo "{}" > tsconfig.json
npm install --save-dev jest ts-jest @types/jest
npm install --save-dev fast-check
mkdir src
#Start vscode
code
The first time I ran the test, it detected a defect: The code allowed transferring zero amounts:
Property failed after 1 tests
{ seed: -1444529403, path: "0:2:0:0:1:0:5:1:3:0:0:1:1:0:1:2:2:0:0:0:0:0", endOnFailure: true }
Counterexample: [0n,0n,0n]
Shrunk 21 time(s)
...stack trace to relevant expect() line in code ....
After fixing that bug it detected another defect: Transfers succeed even when the source account’s balance is insufficient:
Property failed after 4 tests
{ seed: 1922422813, path: "3:0:0:1:0", endOnFailure: true }
Counterexample: [0n,0n,1n]
Shrunk 4 time(s)
...