Finding harder bugs earlier
@Test fun `balance of a single $5 debit is -$5`() {
val txns = listOf(Transaction(type = debit, amount = 500))
assertEquals(-500, balanceOf(txns))
}
@Test
fun `submitting a transaction updates balance`() {
/*
1. open the app, log in
2. tap an account
3. tap "Add Transaction"
4. type 50, tap Submit
5. go back home
*/
assertEquals(Balance(5000), homeScreen.totalBalance())
}
Example tests cover the paths
we thought of.
Bugs live in the paths
we didn't cover.
Example test
"When I type 50 and tap Submit,
balance becomes $50"
Property
"Transaction submit moves the balance exactly by the typed amount"
Specifying and validating invariant properties
// property definition in spec.ts
const submitMovesBalanceByTypedAmount = always(
next(() => {
const typedAmount = parseTypedAmount(txnAmountField.previous?.text);
const on = JSON.stringify(lastAction.on);
if (!on.includes("TxnSubmit")) return true;
if (typedAmount === 0) return true;
return Math.abs(totalBalance.current - totalBalance.previous) === typedAmount;
}))
@Test
fun `adding $50 credit increases balance by $50`() {
// type 50
// tap Submit <- just once, obviously
// go back home
assertEquals(5000, home.totalBalanceCents())
}
passed
We never thought to tap twice. The test didn't either.
Step 14 Tap -> AddTransactionButton
Step 15 InputText -> TxnAmountField "49"
Step 16 Tap -> TxnSubmit
Step 17 Tap -> TxnSubmit
Property violated: submitMovesBalanceByTypedAmount
expected balance change: 4900 cents
actual balance change: 9800 cents <- 2x the amount
violation
Step 17 was random. We never wrote it.
If you want to use this or contribute, please come talk to me.