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) ...