Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Example-based unit tests check whether specific inputs produce expected results. Invariant and property-based tests check whether a rule holds across inputs or operations selected from a defined domain. They complement each other: keep your important examples, then add properties that expose behavior your hand-picked cases may miss.

How property-based testing differs from example-based unit testing

A unit test describes what is being tested; it does not dictate how test inputs must be chosen. In a common example-based test, you write a particular input and its expected output. A property-based test instead states a general rule and describes valid inputs. A framework generates cases from that domain and checks whether the rule holds for them.

Approach What you supply What it helps check
Example-based test A concrete input and expected result A known scenario, boundary case, or regression
Property-based test A rule and a description of valid inputs Whether the rule holds across generated cases

The distinction is about how cases are supplied, not a choice between unit tests and property tests. A property-based test can test a unit, and a test suite can use both approaches. Hypothesis describes property-based testing as an addition to unit testing, with the framework choosing examples—including edge cases—from strategies you define (Hypothesis documentation).

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What is an invariant?

An invariant is a condition that should remain true across the inputs or operations under test. It often captures a relationship that is more useful than a long list of individual expected outputs.

  • Collection size: If a map operation is meant to transform every item without adding or removing any, the output should have the same number of items as the input.
  • Conservation across a transfer: For a modeled transfer between two accounts, their combined balance should remain constant if the operation creates or destroys no funds. Fees and currency conversion need separate, explicit rules.

These properties are only as sound as their assumptions. Define what counts as a valid input and what the operation is supposed to preserve; otherwise, a test may enforce the wrong rule.

Four useful property patterns

Invariants

Check that a condition remains true after an operation or across generated inputs, such as preserving item count or a conserved total.

Inverse operations

Check that one operation reverses another under stated constraints. For example, deserializing serialized data should recover the original value when the format and supported values make that relationship valid. Anthropic uses JSON deserialization as an illustration of this pattern (Anthropic’s property-based testing article).

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Equivalent implementations

If two implementations are intended to have the same semantics, run them on the same generated input and check that their results agree. This is useful only when the implementations are meant to match for the domain being tested.

Idempotence

Check that applying an operation repeatedly has no additional observable effect after the first application. Trimming whitespace is one possible example: trimming an already trimmed string should leave it unchanged.

QuickTheories documents these patterns, including invariants, inverse functions, analogous functions, and idempotence (QuickTheories documentation). Treat them as prompts for specifying behavior, not recipes to apply without checking the product’s requirements. A property should express expected behavior independently enough that it does not simply repeat the implementation’s own logic.

Example: test a sorting rule with separate properties

Suppose a function sorts lists of numbers. Generate lists from a deliberately defined domain, then check at least two distinct properties:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Ordering: Each value appears in the required order in the output.
  • Preservation: The output contains the same elements as the input, including duplicates.

Ordering alone would not catch a function that drops a duplicate; preservation alone would not catch an unsorted result. Also specify how the domain handles special values such as NaN. Whether those values can be ordered, and what “sorted” means for them, requires an explicit decision rather than a broad claim about all numeric inputs.

What generated tests can—and cannot—establish

A property-based framework checks the cases it generates from the domain you describe. This can broaden exploration beyond a short hand-written list, but a finite run does not prove that a property holds for every possible input. An incomplete input domain can miss relevant cases, and an incorrect or overly weak property can pass without checking the behavior you care about.

Hypothesis explains that its strategies describe inputs and that it chooses examples, including edge cases. Its tutorial also notes that a counterexample may not appear on every random run (Hypothesis introduction). Treat a passing run as evidence about the generated cases, not a proof of universal correctness.

When a generated case fails, it is a counterexample to the assertion as written. Some frameworks can simplify failing cases to make them easier to understand; the exact behavior depends on the framework and its version. Check the tool’s documentation rather than assuming all property-testing libraries reduce failures in the same way.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How to add property tests to an existing suite

  1. Choose a behavior with a clear rule. Start with a relationship you can explain plainly, such as preserving a collection’s size or maintaining a total.
  2. Define the valid domain. Decide which inputs are supported and which need special handling. Make exclusions and assumptions explicit.
  3. Write one focused property. Assert the invariant or relationship directly, without duplicating the implementation’s internal logic.
  4. Keep examples for important cases. Retain readable tests for known regressions, boundary conditions, and scenarios whose expected result is valuable in its own right.
  5. Review failures and test cost. Check whether a reported counterexample is reproducible and understandable, and whether the test’s runtime and maintenance fit the suite.

For systems where behavior depends on sequences of actions, ordinary input/output properties may not be enough. Stateful property testing can model operations and check rules across changing state. QuickTheories describes state-related examples, and Fred Hébert’s book Property-Based Testing with PropEr, Erlang, and Elixir covers stateful programs. The book is specifically focused on Erlang and Elixir, not a language-neutral introduction (publisher page).

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Choosing a property-based testing library

Choose a library that fits your language and the way your team tests. Compare these practical considerations rather than relying on an unsupported “best” ranking:

  • Language and test-runner integration: Can the library fit the project’s existing ecosystem and workflow?
  • Generator control: Can you describe the valid domain precisely and generate useful cases for it?
  • Stateful support: Does it support operation sequences if your system requires them?
  • Failure reproduction and reporting: Can you reproduce a failing case and understand what assertion failed?
  • Shrinking or minimization: Does the library reduce failing inputs, and how does that feature work?
  • Documentation and maintenance: Are its current docs clear about the APIs and behaviors you need?

For Python, Hypothesis documents strategies, generated examples, and a broader set of guides and API references. For Java, QuickTheories documents property testing and common property patterns. Library APIs and documentation change, so confirm current details in the official docs before adopting them.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.