In a previous prost, I explained property based testing. In this post we’ll see a simple example using golang’s built-in quick package.
Let assume we’re building the same bank transfer code as described in the dotnet FsCheck earlier post.
Here’s the golang equivalent of the test:
package goquickcheckexample import ( "testing" "testing/quick" ) func TestProperties(t *testing.T) { bank := BuggyBank{} properties := func(StartBalanceA int, StartBalanceB int, TransferAmount int) bool { bank.BalanceOfAccountA = StartBalanceA bank.BalanceOfAccountB = StartBalanceB err := bank.Transfer(TransferAmount) if err != nil { //Transfer failed balancesChanged := (bank.BalanceOfAccountA != StartBalanceA) || (bank.BalanceOfAccountB != StartBalanceB) if balancesChanged { t.Log("Balances changed on failed transfer") } return !balancesChanged } //Transfer succeeded balanceAIsNonNegative := bank.BalanceOfAccountA >= 0 balanceAChanged := bank.BalanceOfAccountA != StartBalanceA balanceBchanged := bank.BalanceOfAccountB != StartBalanceB if !balanceAIsNonNegative { t.Log("Balance of A ended negative") } if !balanceAChanged { t.Log("Balance of A did not change") } if !balanceBchanged { t.Log("Balance of B did not change") } return balanceAIsNonNegative && balanceAChanged && balanceBchanged } c := quick.Config{MaxCount: 1000000} if err := quick.Check(properties, &c); err != nil { t.Error(err) } }
Note: If you want all test runs to use the same set of random numbers then use: c := quick.Config{MaxCount: 1000000, Rand: rand.New(rand.NewSource(0))}
When I ran the test, it detected a defect: Transfers succeed even when the source account’s balance is insufficient:
bank_test.go:28: Balance of A ended negative bank_test.go:41: #2: failed on input 6319534437456565100, -3125004238116898490, 8226184717426742479
After fixing that bug, it detected a defect: The code allowed transferring negative amounts:
bank_test.go:34: Balance of A ended negative bank_test.go:47: #22: failed on input 5995030153294015290, -7891513722668943486, -3464545538278061921
While analyzing this defect we notice yet another one: This code is not safe against integer overflow.