Building Software

Engineering Fundamentals for the Agent Era

Contents Section 2, Verification

Property-Based Testing and Test Quality

Mistakes to catch in review

  1. Code that passes every hand-written example and fails on an input nobody thought of, such as an empty string, a huge number or combining characters.

  2. High line coverage with weak assertions, so deliberately introduced bugs go unnoticed.

  3. A parser or decoder that crashes or hangs on malformed input.

Checking invariants across thousands of generated inputs, and measuring whether your tests would actually catch a bug.

Topics

Properties and Invariants
Round-trip, idempotence, ordering and conservation properties that must hold for every input.
Generators and Shrinking
Producing varied inputs and reducing any failure to its smallest example, with libraries such as fast-check and Hypothesis as examples.
Fuzzing
Feeding random and malformed input to parsers and interfaces to find crashes and security bugs.
Mutation Testing
Introducing small bugs on purpose to measure whether the tests notice them.
Coverage and Its Limits
What line and branch coverage measure, and why a high number can hide a weak suite.

You understand it when you can

  • Write a round-trip property for an encoder and decoder, and let the tool shrink a failing case to its smallest form.
  • Run mutation testing on a module and strengthen the tests that let mutants survive.
  • Explain what code coverage proves and what it does not.

Drill

An agent wrote slug = s => s.toLowerCase().replace(/ /g, '-').replace(/[^a-z0-9-]/g, '') with eight passing example tests, including 'Hello World' becoming 'hello-world'. Write three properties: a slug contains only [a-z0-9-], never starts or ends with a hyphen, and slugging a slug changes nothing. Without running anything, find an input that breaks each property that can be broken, and explain why the others cannot be.

Start here

Watch

Read

Property-Based Testing with PropEr, Erlang, and Elixir: Find Bugs Before Your Users Do

Fred Hebert, 2019.

The most thorough book-length treatment of how to think in properties, write custom generators, read shrunk counterexamples and do stateful testing. The ideas carry over directly to fast-check and Hypothesis.

The Fuzzing Book

Andreas Zeller, Rahul Gopinath and 3 others, 2024. Free to read online.

A free, executable textbook that builds random, mutation-based, grammar-based and coverage-guided fuzzers from scratch, and explains what each one finds in malformed-input handling.

Introduction to Software Testing

Paul Ammann and Jeff Offutt, 2016, 2nd edition.

Defines coverage criteria (statement, branch, logic, mutation) formally, making clear what each criterion guarantees and what a high coverage number leaves untested.

Primary sources