GO / CHAPTER 11
Tests, invariants, and boundary cases
AdvancedChoose checks that can distinguish a correct algorithm from a plausible one.
By the end of this chapter
- Design table-driven cases
- Assert mutation contracts
- Test algebraic and boundary properties
The idea
A useful test targets an observable promise. Table-driven tests collect inputs and expected results so normal, boundary, and invalid cases are easy to compare. Give each case a descriptive name and include the empty case when the function supports it. A test that repeats the implementation’s logic can repeat its bug, so derive the expectation directly from the specification.
Some properties cover many concrete inputs. Sorting should preserve length and the multiset of values while producing nondecreasing order. A nonmutating function should leave its input unchanged even if the returned result is modified later. These are distinct assertions: correct output alone cannot establish input independence. Keep tests deterministic, and control external dependencies when time, randomness, or concurrent scheduling would obscure the behavior under examination.
package main
import "fmt"
func max(xs []int)(int,bool){ if len(xs)==0{return 0,false}; best:=xs[0]; for _,x:=range xs[1:] {if x>best {best=x}}; return best,true }
func main(){ fmt.Println(max([]int{-5,-2,-7})); fmt.Println(max(nil)) }-2 true 0 false
Initializing from the first element handles all-negative input; the boolean distinguishes empty input from a real maximum of zero.
Try it yourself
Write cases for one element, all negative values, duplicate maxima, zero, and empty input.
Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.
CHECK YOUR UNDERSTANDING